diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-08-22 12:55:49 -0600 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-10-21 13:56:55 -0400 |
| commit | d8e3001e535fad178c621ff07418f81f25123dc4 (patch) | |
| tree | 54c03cef2f0bdfd9a3b6df4334c81becb63e4993 | |
| parent | 0e281a30d73c0f31ef9442fef0346779164231ad (diff) | |
Allow multiple high level representations for display, add Pseudo Rust and a Pseudo Python example plugin
42 files changed, 10470 insertions, 174 deletions
diff --git a/architecture.cpp b/architecture.cpp index aa229b4e..a86d7256 100644 --- a/architecture.cpp +++ b/architecture.cpp @@ -60,6 +60,17 @@ InstructionTextToken::InstructionTextToken() : } +InstructionTextToken::InstructionTextToken(uint8_t confidence, BNInstructionTextTokenType t, const string& txt) : + type(t), text(txt), value(0), width(WidthIsByteCount), size(0), operand(BN_INVALID_OPERAND), + context(NoTokenContext), confidence(confidence), address(0), exprIndex(BN_INVALID_EXPR) +{ + if (width == WidthIsByteCount) + { + width = text.size(); + } +} + + InstructionTextToken::InstructionTextToken(BNInstructionTextTokenType t, const std::string& txt, uint64_t val, size_t s, size_t o, uint8_t c, const vector<string>& n, uint64_t w) : type(t), @@ -107,7 +118,7 @@ InstructionTextToken InstructionTextToken::WithConfidence(uint8_t conf) } -static void ConvertInstructionTextToken(const InstructionTextToken& token, BNInstructionTextToken* result) +void InstructionTextToken::ConvertInstructionTextToken(const InstructionTextToken& token, BNInstructionTextToken* result) { result->type = token.type; result->text = BNAllocString(token.text.c_str()); @@ -144,15 +155,19 @@ BNInstructionTextToken* InstructionTextToken::CreateInstructionTextTokenList(con } +void InstructionTextToken::FreeInstructionTextToken(BNInstructionTextToken* token) +{ + BNFreeString(token->text); + for (size_t j = 0; j < token->namesCount; j++) + BNFreeString(token->typeNames[j]); + delete[] token->typeNames; +} + + 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; - } + FreeInstructionTextToken(&tokens[i]); delete[] tokens; } @@ -2285,7 +2300,8 @@ void ArchitectureHook::Register(BNCustomArchitecture* callbacks) string DisassemblyTextRenderer::GetDisplayStringForInteger( Ref<BinaryView> binaryView, BNIntegerDisplayType type, uint64_t value, size_t inputWidth, bool isSigned) { - char* str = BNGetDisplayStringForInteger(binaryView->GetObject(), type, value, inputWidth, isSigned); + char* str = BNGetDisplayStringForInteger(binaryView ? binaryView->GetObject() : nullptr, + type, value, inputWidth, isSigned); string s(str); BNFreeString(str); return s; @@ -2527,6 +2543,24 @@ bool DisassemblyTextRenderer::AddSymbolToken( } +BNSymbolDisplayResult DisassemblyTextRenderer::AddSymbolTokenStatic( + std::vector<InstructionTextToken>& tokens, uint64_t addr, size_t size, size_t operand, + BinaryView* data, size_t maxSymbolWidth, Function* func, uint8_t confidence, + BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence, uint64_t instrAddr, uint64_t exprIndex) +{ + BNInstructionTextToken* result = nullptr; + size_t count = 0; + BNSymbolDisplayResult display = BNGetDisassemblyTextRendererSymbolTokensStatic( + addr, size, operand, data ? data->GetObject() : nullptr, + maxSymbolWidth, func ? func->GetObject() : nullptr, confidence, symbolDisplay, precedence, + instrAddr, exprIndex, &result, &count); + vector<InstructionTextToken> newTokens = + InstructionTextToken::ConvertAndFreeInstructionTextTokenList(result, count); + tokens.insert(tokens.end(), newTokens.begin(), newTokens.end()); + return display; +} + + void DisassemblyTextRenderer::AddStackVariableReferenceTokens( vector<InstructionTextToken>& tokens, const StackVariableReference& ref) { @@ -2560,7 +2594,7 @@ void DisassemblyTextRenderer::AddIntegerToken( vector<InstructionTextToken>& tokens, const InstructionTextToken& token, Architecture* arch, uint64_t addr) { BNInstructionTextToken inToken; - ConvertInstructionTextToken(token, &inToken); + InstructionTextToken::ConvertInstructionTextToken(token, &inToken); size_t count = 0; BNInstructionTextToken* result = @@ -2606,3 +2640,93 @@ void DisassemblyTextRenderer::WrapComment(DisassemblyTextLine& line, vector<Disa BNFreeDisassemblyTextLines(result, count); BNFreeInstructionText(inLine.tokens, inLine.count); } + + +string DisassemblyTextRenderer::GetStringLiteralPrefix(BNStringType type) +{ + char* prefix = BNGetStringLiteralPrefix(type); + string result = prefix; + BNFreeString(prefix); + return result; +} + + +FunctionViewType::FunctionViewType(BNFunctionGraphType viewType) : type(viewType) +{ + if (type == HighLevelLanguageRepresentationFunctionGraph) + name = "Pseudo C"; +} + + +FunctionViewType::FunctionViewType(const BNFunctionViewType& viewType) : type(viewType.type) +{ + if (type == HighLevelLanguageRepresentationFunctionGraph) + { + if (viewType.name) + name = viewType.name; + else + name = "Pseudo C"; + } +} + + +BNFunctionViewType FunctionViewType::ToAPIObject() const +{ + BNFunctionViewType result; + result.type = type; + result.name = nullptr; + if (type == HighLevelLanguageRepresentationFunctionGraph) + result.name = name.c_str(); + return result; +} + + +BNFunctionGraphType FunctionViewType::GetBackingILType() const +{ + if (type == HighLevelLanguageRepresentationFunctionGraph) + return HighLevelILFunctionGraph; + return type; +} + + +bool FunctionViewType::IsValidForView(BinaryView* view) const +{ + if (type != HighLevelLanguageRepresentationFunctionGraph) + return true; + Ref<LanguageRepresentationFunctionType> type = LanguageRepresentationFunctionType::GetByName(name); + if (!type) + return false; + return type->IsValid(view); +} + + +bool FunctionViewType::operator==(const FunctionViewType& other) const +{ + if (type != other.type) + return false; + if (type != HighLevelLanguageRepresentationFunctionGraph) + return true; + return name == other.name; +} + + +bool FunctionViewType::operator!=(const FunctionViewType& other) const +{ + if (type != other.type) + return true; + if (type != HighLevelLanguageRepresentationFunctionGraph) + return false; + return name != other.name; +} + + +bool FunctionViewType::operator<(const FunctionViewType& other) const +{ + if (type < other.type) + return true; + if (type > other.type) + return false; + if (type != HighLevelLanguageRepresentationFunctionGraph) + return false; + return name < other.name; +} diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 0223757c..01f5ba11 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2169,16 +2169,17 @@ namespace BinaryNinja { /*! Convert the contents of the DataBuffer to a string - \param nullTerminates Whether the decoder should stop and return the string after encountering a null (\x00) byte. + \param nullTerminates Whether the decoder should stop and return the string after encountering a null (\x00) + byte. - @threadunsafe + @threadunsafe */ - std::string ToEscapedString(bool nullTerminates = false) const; + std::string ToEscapedString(bool nullTerminates = false, bool escapePrintable = false) const; /*! Create a DataBuffer from a given escaped string. - \param src Input string - \returns Databuffer created from this string + \param src Input string + \returns Databuffer created from this string */ static DataBuffer FromEscapedString(const std::string& src); @@ -2397,7 +2398,9 @@ namespace BinaryNinja { InstructionTextToken(const BNInstructionTextToken& token); InstructionTextToken WithConfidence(uint8_t conf); + static void ConvertInstructionTextToken(const InstructionTextToken& token, BNInstructionTextToken* result); static BNInstructionTextToken* CreateInstructionTextTokenList(const std::vector<InstructionTextToken>& tokens); + static void FreeInstructionTextToken(BNInstructionTextToken* token); static void FreeInstructionTextTokenList( BNInstructionTextToken* tokens, size_t count); static std::vector<InstructionTextToken> ConvertAndFreeInstructionTextTokenList( @@ -3956,6 +3959,29 @@ namespace BinaryNinja { static Ref<Symbol> ImportedFunctionFromImportAddressSymbol(Symbol* sym, uint64_t addr); }; + struct FunctionViewType + { + BNFunctionGraphType type; + std::string name; + + FunctionViewType() : type(NormalFunctionGraph) {} + FunctionViewType(BNFunctionGraphType viewType); + FunctionViewType(const std::string& langName) : + type(HighLevelLanguageRepresentationFunctionGraph), name(langName) + {} + FunctionViewType(const BNFunctionViewType& viewType); + + BNFunctionViewType ToAPIObject() const; + + BNFunctionGraphType GetBackingILType() const; + + bool IsValidForView(BinaryView* view) const; + + bool operator==(const FunctionViewType& other) const; + bool operator!=(const FunctionViewType& other) const; + bool operator<(const FunctionViewType& other) const; + }; + // TODO: This describes how the xref source references the target enum ReferenceType { @@ -6363,29 +6389,29 @@ namespace BinaryNinja { bool FindNextData( uint64_t start, const DataBuffer& data, uint64_t& result, BNFindFlag flags = FindCaseSensitive); bool FindNextText(uint64_t start, const std::string& data, uint64_t& result, Ref<DisassemblySettings> settings, - BNFindFlag flags = FindCaseSensitive, BNFunctionGraphType graph = NormalFunctionGraph); + BNFindFlag flags = FindCaseSensitive, const FunctionViewType& viewType = NormalFunctionGraph); bool FindNextConstant(uint64_t start, uint64_t constant, uint64_t& result, Ref<DisassemblySettings> settings, - BNFunctionGraphType graph = NormalFunctionGraph); + const FunctionViewType& viewType = NormalFunctionGraph); bool FindNextData(uint64_t start, uint64_t end, const DataBuffer& data, uint64_t& addr, BNFindFlag flags, const std::function<bool(size_t current, size_t total)>& progress); bool FindNextText(uint64_t start, uint64_t end, const std::string& data, uint64_t& addr, - Ref<DisassemblySettings> settings, BNFindFlag flags, BNFunctionGraphType graph, + Ref<DisassemblySettings> settings, BNFindFlag flags, const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress); bool FindNextConstant(uint64_t start, uint64_t end, uint64_t constant, uint64_t& addr, - Ref<DisassemblySettings> settings, BNFunctionGraphType graph, + Ref<DisassemblySettings> settings, const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress); bool FindAllData(uint64_t start, uint64_t end, const DataBuffer& data, BNFindFlag flags, const std::function<bool(size_t current, size_t total)>& progress, const std::function<bool(uint64_t addr, const DataBuffer& match)>& matchCallback); bool FindAllText(uint64_t start, uint64_t end, const std::string& data, Ref<DisassemblySettings> settings, - BNFindFlag flags, BNFunctionGraphType graph, + BNFindFlag flags, const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress, const std::function<bool(uint64_t addr, const std::string& match, const LinearDisassemblyLine& line)>& matchCallback); bool FindAllConstant(uint64_t start, uint64_t end, uint64_t constant, Ref<DisassemblySettings> settings, - BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& progress, + const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress, const std::function<bool(uint64_t addr, const LinearDisassemblyLine& line)>& matchCallback); bool Search(const std::string& query, const std::function<bool(uint64_t offset, const DataBuffer& buffer)>& otherCallback); @@ -6819,6 +6845,9 @@ namespace BinaryNinja { bool UserGlobalPointerValueSet() const; void ClearUserGlobalPointerValue(); void SetUserGlobalPointerValue(const Confidence<RegisterValue>& value); + + std::optional<std::pair<std::string, BNStringType>> StringifyUnicodeData( + Architecture* arch, const DataBuffer& buffer, bool allowShortStrings = false); }; class MemoryMap @@ -8801,8 +8830,11 @@ namespace BinaryNinja { Confidence<int64_t> GetStackAdjustment() const; QualifiedName GetStructureName() const; Ref<NamedTypeReference> GetRegisteredName() const; + std::string GetAlternateName() const; uint32_t GetSystemCallNumber() const; BNIntegerDisplayType GetIntegerTypeDisplayType() const; + BNNameType GetNameType() const; + bool ShouldDisplayReturnType() const; uint64_t GetElementCount() const; uint64_t GetOffset() const; @@ -9500,6 +9532,11 @@ namespace BinaryNinja { Ref<Structure> WithReplacedStructure(Structure* from, Structure* to); Ref<Structure> WithReplacedEnumeration(Enumeration* from, Enumeration* to); Ref<Structure> WithReplacedNamedTypeReference(NamedTypeReference* from, NamedTypeReference* to); + + bool ResolveMemberOrBaseMember(BinaryView* data, uint64_t offset, size_t size, + const std::function<void(NamedTypeReference* baseName, Structure* s, size_t memberIndex, + uint64_t structOffset, uint64_t adjustedOffset, const StructureMember& member)>& resolveFunc, + std::optional<size_t> memberIndexHint = std::nullopt); }; /*! StructureBuilder is a convenience class used for building Structure Types. @@ -10419,7 +10456,7 @@ namespace BinaryNinja { ConstantData(BNRegisterValueType state, uint64_t value); ConstantData(BNRegisterValueType state, uint64_t value, size_t size, Ref<Function> func = nullptr); - DataBuffer ToDataBuffer() const; + std::pair<DataBuffer, BNBuiltinType> ToDataBuffer() const; RegisterValue ToRegisterValue() const; }; @@ -10675,7 +10712,8 @@ namespace BinaryNinja { std::set<size_t> GetLowLevelILInstructionsForAddress(Architecture* arch, uint64_t addr); std::vector<size_t> GetLowLevelILExitsForInstruction(Architecture* arch, uint64_t addr); - DataBuffer GetConstantData(BNRegisterValueType state, uint64_t value, size_t size = 0); + std::pair<DataBuffer, BNBuiltinType> GetConstantData( + BNRegisterValueType state, uint64_t value, size_t size = 0); RegisterValue GetRegisterValueAtInstruction(Architecture* arch, uint64_t addr, uint32_t reg); RegisterValue GetRegisterValueAfterInstruction(Architecture* arch, uint64_t addr, uint32_t reg); @@ -10765,8 +10803,9 @@ namespace BinaryNinja { \return The HLIL for this Function if it's available. */ Ref<HighLevelILFunction> GetHighLevelILIfAvailable() const; - Ref<LanguageRepresentationFunction> GetLanguageRepresentation() const; - Ref<LanguageRepresentationFunction> GetLanguageRepresentationIfAvailable() const; + Ref<LanguageRepresentationFunction> GetLanguageRepresentation(const std::string& language = "Pseudo C") const; + Ref<LanguageRepresentationFunction> GetLanguageRepresentationIfAvailable( + const std::string& language = "Pseudo C") const; Ref<Type> GetType() const; Confidence<Ref<Type>> GetReturnType() const; @@ -10807,7 +10846,7 @@ namespace BinaryNinja { void ApplyImportedTypes(Symbol* sym, Ref<Type> type = nullptr); void ApplyAutoDiscoveredType(Type* type); - Ref<FlowGraph> CreateFunctionGraph(BNFunctionGraphType type, DisassemblySettings* settings = nullptr); + Ref<FlowGraph> CreateFunctionGraph(const FunctionViewType& type, DisassemblySettings* settings = nullptr); std::map<int64_t, std::vector<VariableNameAndType>> GetStackLayout(); void CreateAutoStackVariable(int64_t offset, const Confidence<Ref<Type>>& type, const std::string& name); @@ -13280,6 +13319,7 @@ namespace BinaryNinja { }; struct HighLevelILInstruction; + class HighLevelILTokenEmitter; /*! \ingroup highlevelil @@ -13512,6 +13552,8 @@ namespace BinaryNinja { bool IsSSAVarLive(const SSAVariable& var) const; bool IsSSAVarLiveAt(const SSAVariable& var, const size_t instr) const; bool IsVarLiveAt(const Variable& var, const size_t instr) const; + static bool HasSideEffects(const HighLevelILInstruction& instr); + static BNScopeType GetExprScopeType(const HighLevelILInstruction& instr); std::set<size_t> GetVariableSSAVersions(const Variable& var) const; std::set<size_t> GetVariableDefinitions(const Variable& var) const; @@ -13559,15 +13601,254 @@ namespace BinaryNinja { size_t GetExprIndexForLabel(uint64_t label); std::set<size_t> GetUsesForLabel(uint64_t label); + + std::set<Variable> GetVariables(); + std::set<Variable> GetAliasedVariables(); + std::set<SSAVariable> GetSSAVariables(); }; + /*! LanguageRepresentationFunction represents a single function in a registered high level language. + + \ingroup highlevelil + */ class LanguageRepresentationFunction : public CoreRefCountObject<BNLanguageRepresentationFunction, BNNewLanguageRepresentationFunctionReference, BNFreeLanguageRepresentationFunction> { - public: - LanguageRepresentationFunction(Architecture* arch, Function* func = nullptr); + public: + LanguageRepresentationFunction(Architecture* arch, Function* func, HighLevelILFunction* highLevelIL); LanguageRepresentationFunction(BNLanguageRepresentationFunction* func); + + /*! Gets the lines of tokens for a given High Level IL instruction. + + \param instr The instruction to emit lines for. + \param settings The settings for disassembly (optional). + \param asFullAst Whether to emit full AST or single expressions. + \param precedence The current operator precedence level. + \param statement Whether the instruction is a statement or an expression. + \return A list of lines of tokens for the instruction. + */ + std::vector<DisassemblyTextLine> GetExprText(const HighLevelILInstruction& instr, DisassemblySettings* settings, + bool asFullAst = true, BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, + bool statement = false); + + /*! Generates lines for the given High Level IL instruction in the style of the linear view. To get the lines + for the entire function, pass the root instruction of a HighLevelILFunction. + + \param instr The instruction to emit lines for. + \param settings The settings for disassembly (optional). + \param asFullAst Whether to emit full AST or single expressions. + \return A list of lines of tokens for the instruction. + */ + std::vector<DisassemblyTextLine> GetLinearLines( + const HighLevelILInstruction& instr, DisassemblySettings* settings, bool asFullAst = true); + + /*! Generates lines for a single High Level IL basic block. + + \param block The basic block to emit lines for. + \param settings The settings for disassembly (optional). + \return A list of lines of tokens for the basic block. + */ + std::vector<DisassemblyTextLine> GetBlockLines(BasicBlock* block, DisassemblySettings* settings); + + /*! Gets the highlight color for a given basic block. + + \param block The basic block to get the highlight color for. + \return The highlight color for the basic block. + */ + BNHighlightColor GetHighlight(BasicBlock* block); + + Ref<Architecture> GetArchitecture() const; + Ref<Function> GetFunction() const; + Ref<HighLevelILFunction> GetHighLevelILFunction() const; + + /*! Gets the string representing the start of a comment. + + \return The string representing the start of a comment. + */ + virtual std::string GetCommentStartString() const { return "// "; } + + /*! Gets the string representing the end of a comment. + + \return The string representing the end of a comment. + */ + virtual std::string GetCommentEndString() const { return ""; } + + /*! Gets the string representing the start of an annotation. + + \return The string representing the start of an annotation. + */ + virtual std::string GetAnnotationStartString() const { return "{"; } + + /*! Gets the string representing the end of an annotation. + + \return The string representing the end of an annotation. + */ + virtual std::string GetAnnotationEndString() const { return "}"; } + + protected: + /*! Override this method to initialize the options for the token emitter before it is used. + + \param tokens The token emitter to initialize. + */ + virtual void InitTokenEmitter(HighLevelILTokenEmitter& tokens); + + /*! This method must be overridden by all language representation plugins. + + This method is called to emit the tokens for a given High Level IL instruction. + + \param instr The instruction to emit tokens for. + \param tokens The token emitter to use. + \param settings The disassembly settings to use (may be NULL). + \param asFullAst Whether to emit full AST or single expressions. + \param precedence The current operator precedence level. + \param statement Whether the instruction is a statement or an expression. + */ + virtual void GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, + DisassemblySettings* settings, bool asFullAst = true, + BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false) = 0; + + /*! This method can be overridden to emit tokens at the start of a function. + + \param instr The root instruction of the function. + \param tokens The token emitter to use. + */ + virtual void BeginLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens); + + /*! This method can be overridden to emit tokens at the end of a function. + + \param instr The root instruction of the function. + \param tokens The token emitter to use. + */ + virtual void EndLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens); + + private: + static void FreeCallback(void* ctxt); + static void InitTokenEmitterCallback(void* ctxt, BNHighLevelILTokenEmitter* tokens); + static void GetExprTextCallback(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, + BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, bool asFullAst, + BNOperatorPrecedence precedence, bool statement); + static void BeginLinesCallback( + void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens); + static void EndLinesCallback( + void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens); + static char* GetCommentStartStringCallback(void* ctxt); + static char* GetCommentEndStringCallback(void* ctxt); + static char* GetAnnotationStartStringCallback(void* ctxt); + static char* GetAnnotationEndStringCallback(void* ctxt); + }; + + /*! + \ingroup highlevelil + */ + class CoreLanguageRepresentationFunction : public LanguageRepresentationFunction + { + public: + CoreLanguageRepresentationFunction(BNLanguageRepresentationFunction* func); + std::string GetCommentStartString() const override; + std::string GetCommentEndString() const override; + std::string GetAnnotationStartString() const override; + std::string GetAnnotationEndString() const override; + + protected: + void GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, + DisassemblySettings* settings, bool asFullAst = true, + BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false) override; + }; + + class TypePrinter; + class TypeParser; + + /*! LanguageRepresentationFunctionType represents a custom language representation function type. + This class provides methods to create LanguageRepresentationFunction instances for functions, as well + as manage the printing and parsing of types. + + \ingroup highlevelil + */ + class LanguageRepresentationFunctionType : public StaticCoreRefCountObject<BNLanguageRepresentationFunctionType> + { + std::string m_nameForRegister; + + public: + LanguageRepresentationFunctionType(const std::string& name); + LanguageRepresentationFunctionType(BNLanguageRepresentationFunctionType* type); + + std::string GetName() const; + + /*! This method must be overridden. This creates the LanguageRepresentationFunction object for the + given architecture, owner function, and High Level IL function. + + \param arch The architecture of the function. + \param owner The associated function. + \param highLevelIL The High Level IL for the function. + \return A LanguageRepresentationFunction instance for the given function. + */ + virtual Ref<LanguageRepresentationFunction> Create( + Architecture* arch, Function* owner, HighLevelILFunction* highLevelIL) = 0; + + /*! Returns whether the language is valid for the given binary view. + + \param view The binary view to check the validity for. + \return True if the language is valid for the given binary view, false otherwise. + */ + virtual bool IsValid(BinaryView* view); + + /*! Returns the type printer for displaying types in this language. If NULL is returned, the default type + printer will be used. + + \return The optional type printer for displaying types in this language. + */ + virtual Ref<TypePrinter> GetTypePrinter() { return nullptr; } + + /*! Returns the type parser for parsing types in this language. If NULL is returned, the default type + parser will be used. + + \return The optional type parser for parsing types in this language. + */ + virtual Ref<TypeParser> GetTypeParser() { return nullptr; } + + /*! Returns a list of lines representing a function prototype in this language. If no lines are returned, the + default C-style prototype will be used. + + \param func The function to get the prototype lines for. + \param settings The disassembly settings to use (may be NULL). + \return An optional vector of lines representing the function prototype. + */ + virtual std::vector<DisassemblyTextLine> GetFunctionTypeTokens( + Function* func, DisassemblySettings* settings = nullptr); + + /*! Registers the language representation function type. + + \param type The language representation function type to register. + */ + static void Register(LanguageRepresentationFunctionType* type); + + static Ref<LanguageRepresentationFunctionType> GetByName(const std::string& name); + static bool IsValidByName(const std::string& name, BinaryView* view); + static std::vector<Ref<LanguageRepresentationFunctionType>> GetTypes(); + + private: + static BNLanguageRepresentationFunction* CreateCallback( + void* ctxt, BNArchitecture* arch, BNFunction* owner, BNHighLevelILFunction* highLevelIL); + static bool IsValidCallback(void* ctxt, BNBinaryView* view); + static BNTypePrinter* GetTypePrinterCallback(void* ctxt); + static BNTypeParser* GetTypeParserCallback(void* ctxt); + static BNDisassemblyTextLine* GetFunctionTypeTokensCallback( + void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count); + static void FreeLinesCallback(void* ctxt, BNDisassemblyTextLine* lines, size_t count); + }; + + class CoreLanguageRepresentationFunctionType : public LanguageRepresentationFunctionType + { + public: + CoreLanguageRepresentationFunctionType(BNLanguageRepresentationFunctionType* type); + Ref<LanguageRepresentationFunction> Create( + Architecture* arch, Function* owner, HighLevelILFunction* highLevelIL) override; + bool IsValid(BinaryView* view) override; + Ref<TypePrinter> GetTypePrinter() override; + Ref<TypeParser> GetTypeParser() override; + std::vector<DisassemblyTextLine> GetFunctionTypeTokens( + Function* func, DisassemblySettings* settings = nullptr) override; }; /*! @@ -16431,7 +16712,7 @@ namespace BinaryNinja { void* ctxt, BNBinaryView* data, uint64_t addr, BNType* type, BNTypeContext* typeCtx, size_t ctxCount); static BNDisassemblyTextLine* GetLinesForDataCallback(void* ctxt, BNBinaryView* data, uint64_t addr, BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, - BNTypeContext* typeCxt, size_t ctxCount); + BNTypeContext* typeCxt, size_t ctxCount, const char* language); static void FreeCallback(void* ctxt); static void FreeLinesCallback(void* ctxt, BNDisassemblyTextLine* lines, size_t count); @@ -16442,10 +16723,10 @@ namespace BinaryNinja { BinaryView* data, uint64_t addr, Type* type, std::vector<std::pair<Type*, size_t>>& context); virtual std::vector<DisassemblyTextLine> GetLinesForData(BinaryView* data, uint64_t addr, Type* type, const std::vector<InstructionTextToken>& prefix, size_t width, - std::vector<std::pair<Type*, size_t>>& context); + std::vector<std::pair<Type*, size_t>>& context, const std::string& language = std::string()); std::vector<DisassemblyTextLine> RenderLinesForData(BinaryView* data, uint64_t addr, Type* type, const std::vector<InstructionTextToken>& prefix, size_t width, - std::vector<std::pair<Type*, size_t>>& context); + std::vector<std::pair<Type*, size_t>>& context, const std::string& language = std::string()); static bool IsStructOfTypeName( Type* type, const QualifiedName& name, std::vector<std::pair<Type*, size_t>>& context); @@ -16505,6 +16786,12 @@ namespace BinaryNinja { void ResetDeduplicatedComments(); bool AddSymbolToken(std::vector<InstructionTextToken>& tokens, uint64_t addr, size_t size, size_t operand); + static BNSymbolDisplayResult AddSymbolTokenStatic( + std::vector<InstructionTextToken>& tokens, uint64_t addr, size_t size, size_t operand, + BinaryView* data, size_t maxSymbolWidth, Function* func, uint8_t confidence = BN_FULL_CONFIDENCE, + BNSymbolDisplayType symbolDisplay = DisplaySymbolOnly, + BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, + uint64_t instrAddr = -1, uint64_t exprIndex = -1); void AddStackVariableReferenceTokens( std::vector<InstructionTextToken>& tokens, const StackVariableReference& ref); @@ -16516,6 +16803,7 @@ namespace BinaryNinja { bool hasAutoAnnotations, const std::string& leadingSpaces = " ", const std::string& indentSpaces = ""); static std::string GetDisplayStringForInteger(Ref<BinaryView> binaryView, BNIntegerDisplayType type, uint64_t value, size_t inputWidth, bool isSigned = true); + static std::string GetStringLiteralPrefix(BNStringType type); }; /*! @@ -16573,7 +16861,8 @@ namespace BinaryNinja { static Ref<LinearViewObject> CreateMappedMediumLevelILSSAForm(BinaryView* view, DisassemblySettings* settings); static Ref<LinearViewObject> CreateHighLevelIL(BinaryView* view, DisassemblySettings* settings); static Ref<LinearViewObject> CreateHighLevelILSSAForm(BinaryView* view, DisassemblySettings* settings); - static Ref<LinearViewObject> CreateLanguageRepresentation(BinaryView* view, DisassemblySettings* settings); + static Ref<LinearViewObject> CreateLanguageRepresentation(BinaryView* view, DisassemblySettings* settings, + const std::string& language = "Pseudo C"); static Ref<LinearViewObject> CreateDataOnly(BinaryView* view, DisassemblySettings* settings); static Ref<LinearViewObject> CreateSingleFunctionDisassembly(Function* func, DisassemblySettings* settings); @@ -16592,7 +16881,7 @@ namespace BinaryNinja { static Ref<LinearViewObject> CreateSingleFunctionHighLevelILSSAForm( Function* func, DisassemblySettings* settings); static Ref<LinearViewObject> CreateSingleFunctionLanguageRepresentation( - Function* func, DisassemblySettings* settings); + Function* func, DisassemblySettings* settings, const std::string& language = "Pseudo C"); }; /*! @@ -16669,7 +16958,7 @@ namespace BinaryNinja { { BNFindType type; BNFindRangeType rangeType; - BNFunctionGraphType ilType; + FunctionViewType ilType; std::string string; BNFindFlag flags; bool findAll; @@ -18106,6 +18395,210 @@ namespace BinaryNinja { const size_t dataLen ); } // namespace Unicode + + /*! HighLevelILTokenEmitter contains methods for emitting text tokens for High Level IL instructions. + Methods are provided for typical patterns found in various high level languages. + + This class cannot be instantiated directly. An instance of the class will be provided when the methods + in LanguageRepresentationFunction are called. + + \ingroup highlevelil + */ + class HighLevelILTokenEmitter: + public CoreRefCountObject<BNHighLevelILTokenEmitter, BNNewHighLevelILTokenEmitterReference, BNFreeHighLevelILTokenEmitter> + { + public: + HighLevelILTokenEmitter(BNHighLevelILTokenEmitter* emitter); + + class CurrentExprGuard + { + HighLevelILTokenEmitter* m_parent; + BNTokenEmitterExpr m_expr; + + CurrentExprGuard(const CurrentExprGuard&) = delete; + CurrentExprGuard& operator=(const CurrentExprGuard&) = delete; + + public: + CurrentExprGuard(HighLevelILTokenEmitter& parent, const BNTokenEmitterExpr& expr); + ~CurrentExprGuard(); + }; + + /*! Appends a token to the output. */ + template <typename... Args> + void Append(Args&&... args) + { + InstructionTextToken token(std::forward<Args>(args)...); + BNInstructionTextToken converted; + InstructionTextToken::ConvertInstructionTextToken(token, &converted); + BNHighLevelILTokenEmitterAppend(m_object, &converted); + InstructionTextToken::FreeInstructionTextToken(&converted); + } + + /*! Starts a new line in the output. */ + void NewLine(); + + /*! Increases the indentation level by one. */ + void IncreaseIndent(); + + /*! Decreases the indentation level by one. */ + void DecreaseIndent(); + + /*! Indicates that visual separation of scopes is desirable at the current position. By default, + this will insert a blank line, but this can be configured by the user. + */ + void ScopeSeparator(); + + /*! Begins a new scope. Insertion of newlines and braces will be handled using the current settings. + + \param scopeType Type of scope to be started. + */ + void BeginScope(BNScopeType scopeType); + + /*! Ends the current scope. + + \param scopeType Type of scope passed to BeginScope. + */ + void EndScope(BNScopeType scopeType); + + /*! Continues the previous scope with a new associated scope. This is most commonly used for else statements. + + \param forceSameLine If true, the continuation will always be placed on the same line as the previous scope. + */ + void ScopeContinuation(bool forceSameLine); + + /*! Finalizes the previous scope, indicating that there are no more associated scopes. */ + void FinalizeScope(); + + /*! Forces there to be no indentation for the next line. */ + void NoIndentForThisLine(); + + /*! Begins a region of tokens that always have zero confidence. */ + void BeginForceZeroConfidence(); + + /*! Ends a region of tokens that always have zero confidence. */ + void EndForceZeroConfidence(); + + /*! Sets the current expression. When the returned guard object goes out of scope, the previously set + expression becomes active again. + + \param expr Expression to set as the current expression. + \return Guard object to manage the current expression. + */ + CurrentExprGuard SetCurrentExpr(const HighLevelILInstruction& expr); + + /*! Finalizes the outputted lines. */ + void Finalize(); + + void AppendOpenParen(); // ( + void AppendCloseParen(); // ) + void AppendOpenBracket(); // [ + void AppendCloseBracket(); // ] + void AppendOpenBrace(); // { + void AppendCloseBrace(); // } + void AppendSemicolon(); + + /*! Returns the list of tokens on the current line */ + std::vector<InstructionTextToken> GetCurrentTokens() const; + + /*! Sets the requirement for insertion of braces around scopes in the output. */ + void SetBraceRequirement(BNBraceRequirement required); + + /*! Sets whether cases within switch statements should always have braces around them. */ + void SetBracesAroundSwitchCases(bool braces); + + /*! Sets whether braces should default to being on the same line as the statement that begins the scope. + If the user has explicitly set a preference, this setting will be ignored and the user's preference + will be used instead. + */ + void SetDefaultBracesOnSameLine(bool sameLine); + + /*! Sets whether omitting braces around single-line scopes is allowed. */ + void SetSimpleScopeAllowed(bool allowed); + + BNBraceRequirement GetBraceRequirement() const; + bool HasBracesAroundSwitchCases() const; + bool GetDefaultBracesOnSameLine() const; + bool IsSimpleScopeAllowed() const; + + /*! Gets the list of lines in the output. */ + std::vector<DisassemblyTextLine> GetLines() const; + + /*! Appends a size token for the given size in the High Level IL syntax. + + \param size Size in bytes. + \param type Token type to append. + */ + void AppendSizeToken(size_t size, BNInstructionTextTokenType type); + + /*! Appends a floating point size token for the given size in the High Level IL syntax. + + \param size Size in bytes. + \param type Token type to append. + */ + void AppendFloatSizeToken(size_t size, BNInstructionTextTokenType type); + + /*! Appends tokens for access to a variable. + + \param var Variable to access. + \param instr Instruction that accesses the variable. + \param size Size in bytes. + */ + void AppendVarTextToken(const Variable& var, const HighLevelILInstruction& instr, size_t size); + + /*! Appends tokens for a constant intenger value. + + \param instr Instruction that references the value. + \param val Integer value. + \param size Size in bytes. + */ + void AppendIntegerTextToken(const HighLevelILInstruction& instr, int64_t val, size_t size); + + /*! Appends tokens for accessing an array by constant index. + + \param instr Instruction that accesses the array. + \param val Index value. + \param size Size in bytes. + \param address Optional address override. + */ + void AppendArrayIndexToken(const HighLevelILInstruction& instr, int64_t val, size_t size, uint64_t address = 0); + + /*! Appends tokens for displaying a constant pointer value. + + \param instr Instruction that references the pointer. + \param val Pointer value. + \param settings Settings for disassembly (may be NULL). + \param symbolDisplay Symbol display type. + \param precedence Current operator precedence level. + \param allowShortString If true, show as a string even if it is short. + \return Type of symbol resolved if any. + */ + BNSymbolDisplayResult AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val, + DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence, + bool allowShortString = false); + + /*! Appends tokens for a constant value. + + \param instr Instruction that references the value. + \param val Constant value. + \param size Size in bytes. + \param settings Settings for disassembly (may be NULL). + \param precedence Current operator precedence level. + */ + void AppendConstantTextToken(const HighLevelILInstruction& instr, int64_t val, size_t size, + DisassemblySettings* settings, BNOperatorPrecedence precedence); + + /*! Prepends the list of names for the outer structure members when accessing a structure member. This list + can be passed as the list of type names in tokens. + + \param data Binary view associated with the type. + \param type Structure Type being accessed. + \param var Structure variable. + \param nameList Existing list of member names. This list will be updated with the outer structure member + names. + */ + static void AddNamesForOuterStructureMembers( + BinaryView* data, Type* type, const HighLevelILInstruction& var, std::vector<std::string>& nameList); + }; } // namespace BinaryNinja diff --git a/binaryninjacore.h b/binaryninjacore.h index aaf61c6d..93f97dd1 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,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 78 +#define BN_CURRENT_CORE_ABI_VERSION 79 // 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 75 +#define BN_MINIMUM_CORE_ABI_VERSION 79 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -224,6 +224,8 @@ extern "C" typedef struct BNMediumLevelILFunction BNMediumLevelILFunction; typedef struct BNHighLevelILFunction BNHighLevelILFunction; typedef struct BNLanguageRepresentationFunction BNLanguageRepresentationFunction; + typedef struct BNLanguageRepresentationFunctionType BNLanguageRepresentationFunctionType; + typedef struct BNHighLevelILTokenEmitter BNHighLevelILTokenEmitter; typedef struct BNType BNType; typedef struct BNTypeBuilder BNTypeBuilder; typedef struct BNTypeLibrary BNTypeLibrary; @@ -694,6 +696,12 @@ extern "C" HighLevelLanguageRepresentationFunctionGraph = 10, } BNFunctionGraphType; + typedef struct BNFunctionViewType + { + BNFunctionGraphType type; + const char* name; // Only used for HighLevelLanguageRepresentationFunctionGraph + } BNFunctionViewType; + typedef enum BNDisassemblyOption { ShowAddress = 0, @@ -2992,7 +3000,7 @@ extern "C" void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, BNTypeContext* typeCtx, size_t ctxCount); BNDisassemblyTextLine* (*getLinesForData)(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, - BNTypeContext* typeCtx, size_t ctxCount); + BNTypeContext* typeCtx, size_t ctxCount, const char* language); void (*freeLines)(void* ctx, BNDisassemblyTextLine* lines, size_t count); } BNCustomDataRenderer; @@ -3335,6 +3343,108 @@ extern "C" void (*freeVarName)(void* ctxt, BNQualifiedName* name); } BNDemanglerCallbacks; + typedef enum BNScopeType + { + OneLineScopeType, + HasSubScopeScopeType, + BlockScopeType, + SwitchScopeType, + CaseScopeType + } BNScopeType; + + typedef enum BNBraceRequirement + { + OptionalBraces, + BracesNotAllowed, + BracesAlwaysRequired + } BNBraceRequirement; + + typedef struct BNTokenEmitterExpr + { + uint64_t address; + uint32_t sourceOperand; + size_t exprIndex; + size_t instrIndex; + } BNTokenEmitterExpr; + + typedef enum BNOperatorPrecedence + { + TopLevelOperatorPrecedence = 0, + AssignmentOperatorPrecedence, + TernaryOperatorPrecedence, + LogicalOrOperatorPrecedence, + LogicalAndOperatorPrecedence, + BitwiseOrOperatorPrecedence, + BitwiseXorOperatorPrecedence, + BitwiseAndOperatorPrecedence, + EqualityOperatorPrecedence, + CompareOperatorPrecedence, + ShiftOperatorPrecedence, + AddOperatorPrecedence, + SubOperatorPrecedence, + MultiplyOperatorPrecedence, + DivideOperatorPrecedence, + LowUnaryOperatorPrecedence, + UnaryOperatorPrecedence, + HighUnaryOperatorPrecedence, + MemberAndFunctionOperatorPrecedence, + ScopeOperatorPrecedence + } BNOperatorPrecedence; + + typedef enum BNSymbolDisplayType + { + DisplaySymbolOnly, + AddressOfDataSymbols, + DereferenceNonDataSymbols + } BNSymbolDisplayType; + + typedef enum BNSymbolDisplayResult + { + NoSymbolAvailable, + DataSymbolResult, + OtherSymbolResult + } BNSymbolDisplayResult; + + typedef struct BNCustomLanguageRepresentationFunction + { + void* context; + void (*freeObject)(void* ctxt); + void (*externalRefTaken)(void* ctxt); + void (*externalRefReleased)(void* ctxt); + void (*initTokenEmitter)(void* ctxt, BNHighLevelILTokenEmitter* tokens); + void (*getExprText)(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens, + BNDisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement); + void (*beginLines)(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens); + void (*endLines)(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, BNHighLevelILTokenEmitter* tokens); + char* (*getCommentStartString)(void* ctxt); + char* (*getCommentEndString)(void* ctxt); + char* (*getAnnotationStartString)(void* ctxt); + char* (*getAnnotationEndString)(void* ctxt); + } BNCustomLanguageRepresentationFunction; + + typedef struct BNCustomLanguageRepresentationFunctionType + { + void* context; + BNLanguageRepresentationFunction* (*create)( + void* ctxt, BNArchitecture* arch, BNFunction* owner, BNHighLevelILFunction* highLevelIL); + bool (*isValid)(void* ctxt, BNBinaryView* view); + BNTypePrinter* (*getTypePrinter)(void* ctxt); + BNTypeParser* (*getTypeParser)(void* ctxt); + BNDisassemblyTextLine* (*getFunctionTypeTokens)( + void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count); + void (*freeLines)(void* ctxt, BNDisassemblyTextLine* lines, size_t count); + } BNCustomLanguageRepresentationFunctionType; + + typedef enum BNBuiltinType + { + BuiltinNone, + BuiltinMemcpy, + BuiltinMemset, + BuiltinStrncpy, + BuiltinStrcpy, + BuiltinWcscpy + } BNBuiltinType; + typedef bool(*BNProgressFunction)(void*, size_t, size_t); typedef bool(*BNCollaborationAnalysisConflictHandler)(void*, const char** keys, BNAnalysisMergeConflict** conflicts, size_t conflictCount); typedef bool(*BNCollaborationNameChangesetFunction)(void*, BNCollaborationChangeset*); @@ -3504,7 +3614,7 @@ extern "C" BINARYNINJACOREAPI uint8_t BNGetDataBufferByte(BNDataBuffer* buf, size_t offset); BINARYNINJACOREAPI void BNSetDataBufferByte(BNDataBuffer* buf, size_t offset, uint8_t val); - BINARYNINJACOREAPI char* BNDataBufferToEscapedString(BNDataBuffer* buf, bool nullTerminates); + BINARYNINJACOREAPI char* BNDataBufferToEscapedString(BNDataBuffer* buf, bool nullTerminates, bool escapePrintable); BINARYNINJACOREAPI BNDataBuffer* BNDecodeEscapedString(const char* str); BINARYNINJACOREAPI char* BNDataBufferToBase64(BNDataBuffer* buf); BINARYNINJACOREAPI BNDataBuffer* BNDecodeBase64(const char* str); @@ -3918,29 +4028,29 @@ extern "C" BINARYNINJACOREAPI bool BNFindNextData( BNBinaryView* view, uint64_t start, BNDataBuffer* data, uint64_t* result, BNFindFlag flags); BINARYNINJACOREAPI bool BNFindNextText(BNBinaryView* view, uint64_t start, const char* data, uint64_t* result, - BNDisassemblySettings* settings, BNFindFlag flags, BNFunctionGraphType graph); + BNDisassemblySettings* settings, BNFindFlag flags, BNFunctionViewType viewType); BINARYNINJACOREAPI bool BNFindNextConstant(BNBinaryView* view, uint64_t start, uint64_t constant, uint64_t* result, - BNDisassemblySettings* settings, BNFunctionGraphType graph); + BNDisassemblySettings* settings, BNFunctionViewType viewType); BINARYNINJACOREAPI bool BNFindNextDataWithProgress(BNBinaryView* view, uint64_t start, uint64_t end, BNDataBuffer* data, uint64_t* result, BNFindFlag flags, void* ctxt, BNProgressFunction progress); BINARYNINJACOREAPI bool BNFindNextTextWithProgress(BNBinaryView* view, uint64_t start, uint64_t end, const char* data, uint64_t* result, BNDisassemblySettings* settings, BNFindFlag flags, - BNFunctionGraphType graph, void* ctxt, BNProgressFunction progress); + BNFunctionViewType viewType, void* ctxt, BNProgressFunction progress); BINARYNINJACOREAPI bool BNFindNextConstantWithProgress(BNBinaryView* view, uint64_t start, uint64_t end, - uint64_t constant, uint64_t* result, BNDisassemblySettings* settings, BNFunctionGraphType graph, void* ctxt, + uint64_t constant, uint64_t* result, BNDisassemblySettings* settings, BNFunctionViewType viewType, void* ctxt, BNProgressFunction progress); BINARYNINJACOREAPI bool BNFindAllDataWithProgress(BNBinaryView* view, uint64_t start, uint64_t end, BNDataBuffer* data, BNFindFlag flags, void* ctxt, BNProgressFunction progress, void* matchCtxt, bool (*matchCallback)(void* matchCtxt, uint64_t addr, BNDataBuffer* match)); BINARYNINJACOREAPI bool BNFindAllTextWithProgress(BNBinaryView* view, uint64_t start, uint64_t end, - const char* data, BNDisassemblySettings* settings, BNFindFlag flags, BNFunctionGraphType graph, void* ctxt, + const char* data, BNDisassemblySettings* settings, BNFindFlag flags, BNFunctionViewType viewType, void* ctxt, BNProgressFunction progress, void* matchCtxt, bool (*matchCallback)(void* matchCtxt, uint64_t addr, const char* match, BNLinearDisassemblyLine* line)); BINARYNINJACOREAPI bool BNFindAllConstantWithProgress(BNBinaryView* view, uint64_t start, uint64_t end, - uint64_t constant, BNDisassemblySettings* settings, BNFunctionGraphType graph, void* ctxt, + uint64_t constant, BNDisassemblySettings* settings, BNFunctionViewType viewType, void* ctxt, BNProgressFunction progress, void* matchCtxt, bool (*matchCallback)(void* matchCtxt, uint64_t addr, BNLinearDisassemblyLine* line)); @@ -3996,6 +4106,9 @@ extern "C" BINARYNINJACOREAPI void BNClearUserGlobalPointerValue(BNBinaryView* view); BINARYNINJACOREAPI void BNSetUserGlobalPointerValue(BNBinaryView* view, BNRegisterValueWithConfidence value); + BINARYNINJACOREAPI bool BNStringifyUnicodeData(BNBinaryView* data, BNArchitecture* arch, const BNDataBuffer* buffer, + bool allowShortStrings, char** string, BNStringType* type); + // Raw binary data view BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataView(BNFileMetadata* file); BINARYNINJACOREAPI BNBinaryView* BNCreateBinaryDataViewFromBuffer(BNFileMetadata* file, BNDataBuffer* buf); @@ -4370,10 +4483,13 @@ extern "C" BINARYNINJACOREAPI BNMediumLevelILFunction* BNGetFunctionMappedMediumLevelILIfAvailable(BNFunction* func); BINARYNINJACOREAPI BNHighLevelILFunction* BNGetFunctionHighLevelIL(BNFunction* func); BINARYNINJACOREAPI BNHighLevelILFunction* BNGetFunctionHighLevelILIfAvailable(BNFunction* func); - BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentation(BNFunction* func); - BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentationIfAvailable(BNFunction* func); + BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentation( + BNFunction* func, const char* language); + BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentationIfAvailable( + BNFunction* func, const char* language); - BINARYNINJACOREAPI BNDataBuffer* BNGetConstantData(BNFunction* func, BNRegisterValueType state, uint64_t value, size_t size); + BINARYNINJACOREAPI BNDataBuffer* BNGetConstantData(BNFunction* func, BNRegisterValueType state, uint64_t value, + size_t size, BNBuiltinType* builtin); BINARYNINJACOREAPI BNRegisterValue BNGetRegisterValueAtInstruction( BNFunction* func, BNArchitecture* arch, uint64_t addr, uint32_t reg); @@ -4556,6 +4672,10 @@ extern "C" BINARYNINJACOREAPI void BNResetDisassemblyTextRendererDeduplicatedComments(BNDisassemblyTextRenderer* renderer); BINARYNINJACOREAPI bool BNGetDisassemblyTextRendererSymbolTokens(BNDisassemblyTextRenderer* renderer, uint64_t addr, size_t size, size_t operand, BNInstructionTextToken** result, size_t* count); + BINARYNINJACOREAPI BNSymbolDisplayResult BNGetDisassemblyTextRendererSymbolTokensStatic(uint64_t addr, size_t size, + size_t operand, BNBinaryView* data, size_t maxSymbolWidth, BNFunction* func, uint8_t confidence, + BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence, uint64_t instrAddr, uint64_t exprIndex, + BNInstructionTextToken** result, size_t* count); BINARYNINJACOREAPI BNInstructionTextToken* BNGetDisassemblyTextRendererStackVariableReferenceTokens( BNDisassemblyTextRenderer* renderer, BNStackVariableReference* ref, size_t* count); BINARYNINJACOREAPI bool BNIsIntegerToken(BNInstructionTextTokenType type); @@ -4565,6 +4685,7 @@ extern "C" BINARYNINJACOREAPI BNDisassemblyTextLine* BNDisassemblyTextRendererWrapComment(BNDisassemblyTextRenderer* renderer, const BNDisassemblyTextLine* inLine, size_t* outLineCount, const char* comment, bool hasAutoAnnotations, const char* leadingSpaces, const char* indentSpaces); + BINARYNINJACOREAPI char* BNGetStringLiteralPrefix(BNStringType type); BINARYNINJACOREAPI void BNMarkFunctionAsRecentlyUsed(BNFunction* func); BINARYNINJACOREAPI void BNMarkBasicBlockAsRecentlyUsed(BNBasicBlock* block); @@ -4796,7 +4917,7 @@ extern "C" BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewHighLevelILSSAForm( BNBinaryView* view, BNDisassemblySettings* settings); BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewLanguageRepresentation( - BNBinaryView* view, BNDisassemblySettings* settings); + BNBinaryView* view, BNDisassemblySettings* settings, const char* language); BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewDataOnly( BNBinaryView* view, BNDisassemblySettings* settings); BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewSingleFunctionDisassembly( @@ -4820,7 +4941,7 @@ extern "C" BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewSingleFunctionHighLevelILSSAForm( BNFunction* func, BNDisassemblySettings* settings); BINARYNINJACOREAPI BNLinearViewObject* BNCreateLinearViewSingleFunctionLanguageRepresentation( - BNFunction* func, BNDisassemblySettings* settings); + BNFunction* func, BNDisassemblySettings* settings, const char* language); BINARYNINJACOREAPI BNLinearViewObject* BNNewLinearViewObjectReference(BNLinearViewObject* obj); BINARYNINJACOREAPI void BNFreeLinearViewObject(BNLinearViewObject* obj); BINARYNINJACOREAPI BNLinearViewObject* BNGetFirstLinearViewObjectChild(BNLinearViewObject* obj); @@ -5245,7 +5366,7 @@ extern "C" // Flow graphs BINARYNINJACOREAPI BNFlowGraph* BNCreateFlowGraph(void); BINARYNINJACOREAPI BNFlowGraph* BNCreateFunctionGraph( - BNFunction* func, BNFunctionGraphType type, BNDisassemblySettings* settings); + BNFunction* func, BNFunctionViewType type, BNDisassemblySettings* settings); BINARYNINJACOREAPI BNFlowGraph* BNCreateLowLevelILFunctionGraph( BNLowLevelILFunction* func, BNDisassemblySettings* settings); BINARYNINJACOREAPI BNFlowGraph* BNCreateMediumLevelILFunctionGraph( @@ -5790,6 +5911,8 @@ extern "C" BNHighLevelILFunction* func, const BNVariable* var, const size_t version, const size_t instr); BINARYNINJACOREAPI bool BNIsHighLevelILVarLiveAt( BNHighLevelILFunction* func, const BNVariable* var, const size_t instr); + BINARYNINJACOREAPI bool BNHighLevelILHasSideEffects(BNHighLevelILFunction* func, size_t exprIndex); + BINARYNINJACOREAPI BNScopeType BNGetHighLevelILExprScopeType(BNHighLevelILFunction* func, size_t exprIndex); BINARYNINJACOREAPI BNVariable* BNGetHighLevelILVariables(BNHighLevelILFunction* func, size_t* count); BINARYNINJACOREAPI BNVariable* BNGetHighLevelILAliasedVariables(BNHighLevelILFunction* func, size_t* count); @@ -5895,12 +6018,49 @@ extern "C" BNBinaryView* view, const BNQualifiedName* typeName, BNTypeLibrary** lib, BNQualifiedName* resultName); // Language Representation + BINARYNINJACOREAPI BNLanguageRepresentationFunctionType* BNRegisterLanguageRepresentationFunctionType( + const char* name, BNCustomLanguageRepresentationFunctionType* type); + BINARYNINJACOREAPI BNLanguageRepresentationFunctionType* BNGetLanguageRepresentationFunctionTypeByName(const char* name); + BINARYNINJACOREAPI BNLanguageRepresentationFunctionType** BNGetLanguageRepresentationFunctionTypeList(size_t* count); + BINARYNINJACOREAPI void BNFreeLanguageRepresentationFunctionTypeList(BNLanguageRepresentationFunctionType** types); + BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionTypeName(BNLanguageRepresentationFunctionType* type); BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNCreateLanguageRepresentationFunction( - BNArchitecture* arch, BNFunction* func); + BNLanguageRepresentationFunctionType* type, BNArchitecture* arch, BNFunction* func, + BNHighLevelILFunction* highLevelIL); + BINARYNINJACOREAPI bool BNIsLanguageRepresentationFunctionTypeValid( + BNLanguageRepresentationFunctionType* type, BNBinaryView* view); + BINARYNINJACOREAPI BNTypePrinter* BNGetLanguageRepresentationFunctionTypePrinter(BNLanguageRepresentationFunctionType* type); + BINARYNINJACOREAPI BNTypeParser* BNGetLanguageRepresentationFunctionTypeParser(BNLanguageRepresentationFunctionType* type); + BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens( + BNLanguageRepresentationFunctionType* type, BNFunction* func, BNDisassemblySettings* settings, size_t* count); + + BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNCreateCustomLanguageRepresentationFunction( + BNArchitecture* arch, BNFunction* func, BNHighLevelILFunction* highLevelIL, + BNCustomLanguageRepresentationFunction* callbacks); BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNNewLanguageRepresentationFunctionReference( - BNLanguageRepresentationFunction* func); + BNLanguageRepresentationFunction* func); BINARYNINJACOREAPI void BNFreeLanguageRepresentationFunction(BNLanguageRepresentationFunction* func); + BINARYNINJACOREAPI BNArchitecture* BNGetLanguageRepresentationArchitecture(BNLanguageRepresentationFunction* func); BINARYNINJACOREAPI BNFunction* BNGetLanguageRepresentationOwnerFunction(BNLanguageRepresentationFunction* func); + BINARYNINJACOREAPI BNHighLevelILFunction* BNGetLanguageRepresentationILFunction(BNLanguageRepresentationFunction* func); + BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionExprText( + BNLanguageRepresentationFunction* func, BNHighLevelILFunction* il, size_t exprIndex, + BNDisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement, size_t* count); + BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionLinearLines( + BNLanguageRepresentationFunction* func, BNHighLevelILFunction* il, size_t exprIndex, + BNDisassemblySettings* settings, bool asFullAst, size_t* count); + BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLanguageRepresentationFunctionBlockLines( + BNLanguageRepresentationFunction* func, BNBasicBlock* block, BNDisassemblySettings* settings, size_t* count); + BINARYNINJACOREAPI BNHighlightColor BNGetLanguageRepresentationFunctionHighlight( + BNLanguageRepresentationFunction* func, BNBasicBlock* block); + BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionCommentStartString( + BNLanguageRepresentationFunction* func); + BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionCommentEndString( + BNLanguageRepresentationFunction* func); + BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionAnnotationStartString( + BNLanguageRepresentationFunction* func); + BINARYNINJACOREAPI char* BNGetLanguageRepresentationFunctionAnnotationEndString( + BNLanguageRepresentationFunction* func); // Types BINARYNINJACOREAPI bool BNTypesEqual(BNType* a, BNType* b); @@ -5996,6 +6156,7 @@ extern "C" BINARYNINJACOREAPI BNReferenceType BNTypeGetReferenceType(BNType* type); BINARYNINJACOREAPI BNPointerBaseType BNTypeGetPointerBaseType(BNType* type); BINARYNINJACOREAPI int64_t BNTypeGetPointerBaseOffset(BNType* type); + BINARYNINJACOREAPI BNNameType BNTypeGetNameType(BNType* type); BINARYNINJACOREAPI char* BNGetTypeAlternateName(BNType* type); BINARYNINJACOREAPI uint32_t BNTypeGetSystemCallNumber(BNType* type); BINARYNINJACOREAPI bool BNTypeIsSystemCall(BNType* type); @@ -6003,6 +6164,7 @@ extern "C" BINARYNINJACOREAPI char* BNGetTypePointerSuffixString(BNType* type); BINARYNINJACOREAPI BNInstructionTextToken* BNGetTypePointerSuffixTokens(BNType* type, uint8_t baseConfidence, size_t* count); BINARYNINJACOREAPI void BNFreePointerSuffixList(BNPointerSuffix* suffix, size_t count); + BINARYNINJACOREAPI bool BNTypeShouldDisplayReturnType(BNType* type); BINARYNINJACOREAPI char* BNGetTypeString(BNType* type, BNPlatform* platform, BNTokenEscapingType escaping); BINARYNINJACOREAPI char* BNGetTypeStringBeforeName(BNType* type, BNPlatform* platform, BNTokenEscapingType escaping); @@ -6155,6 +6317,11 @@ extern "C" BINARYNINJACOREAPI BNStructureVariant BNGetStructureType(BNStructure* s); BINARYNINJACOREAPI BNBaseStructure* BNGetBaseStructuresForStructure(BNStructure* s, size_t* count); BINARYNINJACOREAPI void BNFreeBaseStructureList(BNBaseStructure* bases, size_t count); + BINARYNINJACOREAPI bool BNResolveStructureMemberOrBaseMember(BNStructure* s, BNBinaryView* data, uint64_t offset, + size_t size, void* callbackContext, + void (*resolveFunc)(void* ctxt, BNNamedTypeReference* baseName, BNStructure* resolvedStruct, size_t memberIndex, + uint64_t structOffset, uint64_t adjustedOffset, BNStructureMember member), + bool memberIndexHintValid, size_t memberIndexHint); BINARYNINJACOREAPI BNStructure* BNStructureWithReplacedStructure( BNStructure* s, BNStructure* from, BNStructure* to); @@ -7120,10 +7287,10 @@ extern "C" void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, BNTypeContext* typeCtx, size_t ctxCount); BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLinesForData(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, - BNTypeContext* typeCtx, size_t ctxCount); + BNTypeContext* typeCtx, size_t ctxCount, const char* language); BINARYNINJACOREAPI BNDisassemblyTextLine* BNRenderLinesForData(BNBinaryView* data, uint64_t addr, BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, BNTypeContext* typeCtx, - size_t ctxCount); + size_t ctxCount, const char* language); BINARYNINJACOREAPI void BNFreeDataRenderer(BNDataRenderer* renderer); BINARYNINJACOREAPI BNDataRendererContainer* BNGetDataRendererContainer(void); BINARYNINJACOREAPI void BNRegisterGenericDataRenderer(BNDataRendererContainer* container, BNDataRenderer* renderer); @@ -7682,6 +7849,66 @@ extern "C" BINARYNINJACOREAPI bool BNAnalysisMergeConflictSplitterCanSplit(BNAnalysisMergeConflictSplitter* splitter, const char* key, BNAnalysisMergeConflict* conflict); BINARYNINJACOREAPI bool BNAnalysisMergeConflictSplitterSplit(BNAnalysisMergeConflictSplitter* splitter, const char* originalKey, BNAnalysisMergeConflict* originalConflict, BNKeyValueStore* result, char*** newKeys, BNAnalysisMergeConflict*** newConflicts, size_t* newCount); + // High level token emitter + BINARYNINJACOREAPI BNHighLevelILTokenEmitter* BNNewHighLevelILTokenEmitterReference(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNFreeHighLevelILTokenEmitter(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppend(BNHighLevelILTokenEmitter* emitter, BNInstructionTextToken* token); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterNewLine(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterIncreaseIndent(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterDecreaseIndent(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterScopeSeparator(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterBeginScope(BNHighLevelILTokenEmitter* emitter, BNScopeType type); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterEndScope(BNHighLevelILTokenEmitter* emitter, BNScopeType type); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterScopeContinuation(BNHighLevelILTokenEmitter* emitter, bool forceSameLine); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterFinalizeScope(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterNoIndentForThisLine(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterBeginForceZeroConfidence(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterEndForceZeroConfidence(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI BNTokenEmitterExpr BNHighLevelILTokenEmitterSetCurrentExpr(BNHighLevelILTokenEmitter* emitter, BNTokenEmitterExpr expr); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterRestoreCurrentExpr(BNHighLevelILTokenEmitter* emitter, BNTokenEmitterExpr expr); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterFinalize(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendOpenParen(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendCloseParen(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendOpenBracket(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendCloseBracket(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendOpenBrace(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendCloseBrace(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterAppendSemicolon(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetBraceRequirement( + BNHighLevelILTokenEmitter* emitter, BNBraceRequirement required); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(BNHighLevelILTokenEmitter* emitter, bool braces); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(BNHighLevelILTokenEmitter* emitter, bool sameLine); + BINARYNINJACOREAPI void BNHighLevelILTokenEmitterSetSimpleScopeAllowed(BNHighLevelILTokenEmitter* emitter, bool allowed); + BINARYNINJACOREAPI BNBraceRequirement BNHighLevelILTokenEmitterGetBraceRequirement( + BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI bool BNHighLevelILTokenEmitterIsSimpleScopeAllowed(BNHighLevelILTokenEmitter* emitter); + BINARYNINJACOREAPI BNInstructionTextToken* BNHighLevelILTokenEmitterGetCurrentTokens(BNHighLevelILTokenEmitter* emitter, size_t* tokenCount); + BINARYNINJACOREAPI BNDisassemblyTextLine* BNHighLevelILTokenEmitterGetLines(BNHighLevelILTokenEmitter* emitter, size_t* count); + + BINARYNINJACOREAPI void BNAddHighLevelILSizeToken(size_t size, BNInstructionTextTokenType type, BNHighLevelILTokenEmitter* tokens); + BINARYNINJACOREAPI void BNAddHighLevelILFloatSizeToken(size_t size, BNInstructionTextTokenType type, BNHighLevelILTokenEmitter* tokens); + BINARYNINJACOREAPI void BNAddHighLevelILVarTextToken(BNHighLevelILFunction* func, const BNVariable* var, + BNHighLevelILTokenEmitter* tokens, size_t exprIndex, size_t size); + BINARYNINJACOREAPI void BNAddHighLevelILIntegerTextToken(BNHighLevelILFunction* func, size_t exprIndex, + int64_t val, size_t size, BNHighLevelILTokenEmitter* tokens); + BINARYNINJACOREAPI void BNAddHighLevelILArrayIndexToken(BNHighLevelILFunction* func, size_t exprIndex, int64_t val, + size_t size, BNHighLevelILTokenEmitter* tokens, uint64_t address); + BINARYNINJACOREAPI BNSymbolDisplayResult BNAddHighLevelILPointerTextToken(BNHighLevelILFunction* func, + size_t exprIndex, int64_t val, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, + BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence, bool allowShortString); + BINARYNINJACOREAPI void BNAddHighLevelILConstantTextToken(BNHighLevelILFunction* func, size_t exprIndex, + int64_t val, size_t size, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, + BNOperatorPrecedence precedence); + BINARYNINJACOREAPI char** BNAddNamesForOuterStructureMembers(BNBinaryView* data, BNType* type, + BNHighLevelILFunction* hlil, size_t varExpr, size_t* nameCount); + + BINARYNINJACOREAPI int64_t BNZeroExtend(int64_t value, size_t sourceSize, size_t destSize); + BINARYNINJACOREAPI int64_t BNSignExtend(int64_t value, size_t sourceSize, size_t destSize); + BINARYNINJACOREAPI int64_t BNMaskToSize(int64_t value, size_t size); + BINARYNINJACOREAPI int64_t BNGetMaskForSize(size_t size); + #ifdef __cplusplus } #endif diff --git a/binaryview.cpp b/binaryview.cpp index 510d74a5..d2dbee56 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -4533,15 +4533,15 @@ bool BinaryView::FindNextData(uint64_t start, const DataBuffer& data, uint64_t& } bool BinaryView::FindNextText(uint64_t start, const std::string& data, uint64_t& result, - Ref<DisassemblySettings> settings, BNFindFlag flags, BNFunctionGraphType graph) + Ref<DisassemblySettings> settings, BNFindFlag flags, const FunctionViewType& viewType) { - return BNFindNextText(m_object, start, data.c_str(), &result, settings->GetObject(), flags, graph); + return BNFindNextText(m_object, start, data.c_str(), &result, settings->GetObject(), flags, viewType.ToAPIObject()); } bool BinaryView::FindNextConstant( - uint64_t start, uint64_t constant, uint64_t& result, Ref<DisassemblySettings> settings, BNFunctionGraphType graph) + uint64_t start, uint64_t constant, uint64_t& result, Ref<DisassemblySettings> settings, const FunctionViewType& viewType) { - return BNFindNextConstant(m_object, start, constant, &result, settings->GetObject(), graph); + return BNFindNextConstant(m_object, start, constant, &result, settings->GetObject(), viewType.ToAPIObject()); } @@ -4603,24 +4603,24 @@ bool BinaryView::FindNextData(uint64_t start, uint64_t end, const DataBuffer& da bool BinaryView::FindNextText(uint64_t start, uint64_t end, const std::string& data, uint64_t& addr, - Ref<DisassemblySettings> settings, BNFindFlag flags, BNFunctionGraphType graph, + Ref<DisassemblySettings> settings, BNFindFlag flags, const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress) { ProgressContext fp; fp.callback = progress; return BNFindNextTextWithProgress( - m_object, start, end, data.c_str(), &addr, settings->GetObject(), flags, graph, &fp, ProgressCallback); + m_object, start, end, data.c_str(), &addr, settings->GetObject(), flags, viewType.ToAPIObject(), &fp, ProgressCallback); } bool BinaryView::FindNextConstant(uint64_t start, uint64_t end, uint64_t constant, uint64_t& addr, - Ref<DisassemblySettings> settings, BNFunctionGraphType graph, + Ref<DisassemblySettings> settings, const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress) { ProgressContext fp; fp.callback = progress; return BNFindNextConstantWithProgress( - m_object, start, end, constant, &addr, settings->GetObject(), graph, &fp, ProgressCallback); + m_object, start, end, constant, &addr, settings->GetObject(), viewType.ToAPIObject(), &fp, ProgressCallback); } @@ -4638,7 +4638,7 @@ bool BinaryView::FindAllData(uint64_t start, uint64_t end, const DataBuffer& dat bool BinaryView::FindAllText(uint64_t start, uint64_t end, const std::string& data, Ref<DisassemblySettings> settings, - BNFindFlag flags, BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& progress, + BNFindFlag flags, const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress, const std::function<bool(uint64_t addr, const std::string& match, const LinearDisassemblyLine& line)>& matchCallback) { @@ -4646,20 +4646,20 @@ bool BinaryView::FindAllText(uint64_t start, uint64_t end, const std::string& da fp.callback = progress; MatchCallbackContextForText mc; mc.func = matchCallback; - return BNFindAllTextWithProgress(m_object, start, end, data.c_str(), settings->GetObject(), flags, graph, &fp, + return BNFindAllTextWithProgress(m_object, start, end, data.c_str(), settings->GetObject(), flags, viewType.ToAPIObject(), &fp, ProgressCallback, &mc, MatchCallbackForText); } bool BinaryView::FindAllConstant(uint64_t start, uint64_t end, uint64_t constant, Ref<DisassemblySettings> settings, - BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& progress, + const FunctionViewType& viewType, const std::function<bool(size_t current, size_t total)>& progress, const std::function<bool(uint64_t addr, const LinearDisassemblyLine& line)>& matchCallback) { ProgressContext fp; fp.callback = progress; MatchCallbackContextForConstant mc; mc.func = matchCallback; - return BNFindAllConstantWithProgress(m_object, start, end, constant, settings->GetObject(), graph, &fp, + return BNFindAllConstantWithProgress(m_object, start, end, constant, settings->GetObject(), viewType.ToAPIObject(), &fp, ProgressCallback, &mc, MatchCallbackForConstant); } @@ -5316,6 +5316,20 @@ void BinaryView::SetUserGlobalPointerValue(const Confidence<RegisterValue>& valu } +optional<pair<string, BNStringType>> BinaryView::StringifyUnicodeData(Architecture* arch, + const DataBuffer& buffer, bool allowShortStrings) +{ + char* str = nullptr; + BNStringType type = AsciiString; + if (!BNStringifyUnicodeData(m_object, arch ? arch->GetObject() : nullptr, buffer.GetBufferObject(), + allowShortStrings, &str, &type)) + return nullopt; + + string result(str); + BNFreeString(str); + return make_pair(result, type); +} + Relocation::Relocation(BNRelocation* reloc) { diff --git a/databuffer.cpp b/databuffer.cpp index 6f9c3258..f8f15bd4 100644 --- a/databuffer.cpp +++ b/databuffer.cpp @@ -190,9 +190,9 @@ const uint8_t& DataBuffer::operator[](size_t offset) const } -string DataBuffer::ToEscapedString(bool nullTerminates) const +string DataBuffer::ToEscapedString(bool nullTerminates, bool escapePrintable) const { - char* str = BNDataBufferToEscapedString(m_buffer, nullTerminates); + char* str = BNDataBufferToEscapedString(m_buffer, nullTerminates, escapePrintable); string result = str; BNFreeString(str); return result; diff --git a/datarenderer.cpp b/datarenderer.cpp index a2ed1dae..4e4a3346 100644 --- a/datarenderer.cpp +++ b/datarenderer.cpp @@ -64,7 +64,7 @@ bool DataRenderer::IsValidForDataCallback( BNDisassemblyTextLine* DataRenderer::GetLinesForDataCallback(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, - BNTypeContext* typeCtx, size_t ctxCount) + BNTypeContext* typeCtx, size_t ctxCount, const char* language) { CallbackRef<DataRenderer> renderer(ctxt); Ref<BinaryView> viewObj = new BinaryView(BNNewViewReference(view)); @@ -80,7 +80,8 @@ BNDisassemblyTextLine* DataRenderer::GetLinesForDataCallback(void* ctxt, BNBinar contextType->AddRef(); context.push_back({contextType, typeCtx[i].offset}); } - auto lines = renderer->GetLinesForData(viewObj, addr, typeObj, prefixes, width, context); + auto lines = renderer->GetLinesForData(viewObj, addr, typeObj, prefixes, width, context, + language ? language : string()); *count = lines.size(); BNDisassemblyTextLine* buf = new BNDisassemblyTextLine[lines.size()]; for (size_t i = 0; i < lines.size(); i++) @@ -128,7 +129,7 @@ bool DataRenderer::IsValidForData(BinaryView* data, uint64_t addr, Type* type, v vector<DisassemblyTextLine> DataRenderer::GetLinesForData(BinaryView* data, uint64_t addr, Type* type, - const std::vector<InstructionTextToken>& prefix, size_t width, vector<pair<Type*, size_t>>& context) + const std::vector<InstructionTextToken>& prefix, size_t width, vector<pair<Type*, size_t>>& context, const string& language) { BNInstructionTextToken* prefixes = InstructionTextToken::CreateInstructionTextTokenList(prefix); BNTypeContext* typeCtx = new BNTypeContext[context.size()]; @@ -139,7 +140,7 @@ vector<DisassemblyTextLine> DataRenderer::GetLinesForData(BinaryView* data, uint } size_t count = 0; BNDisassemblyTextLine* lines = BNGetLinesForData(m_object, data->GetObject(), addr, type->GetObject(), prefixes, - prefix.size(), width, &count, typeCtx, context.size()); + prefix.size(), width, &count, typeCtx, context.size(), language.c_str()); delete[] typeCtx; for (size_t i = 0; i < prefix.size(); i++) @@ -168,7 +169,7 @@ vector<DisassemblyTextLine> DataRenderer::GetLinesForData(BinaryView* data, uint vector<DisassemblyTextLine> DataRenderer::RenderLinesForData(BinaryView* data, uint64_t addr, Type* type, - const std::vector<InstructionTextToken>& prefix, size_t width, vector<pair<Type*, size_t>>& context) + const std::vector<InstructionTextToken>& prefix, size_t width, vector<pair<Type*, size_t>>& context, const string& language) { BNInstructionTextToken* prefixes = InstructionTextToken::CreateInstructionTextTokenList(prefix); BNTypeContext* typeCtx = new BNTypeContext[context.size()]; @@ -179,7 +180,8 @@ vector<DisassemblyTextLine> DataRenderer::RenderLinesForData(BinaryView* data, u } size_t count = 0; BNDisassemblyTextLine* lines = BNRenderLinesForData( - data->GetObject(), addr, type->GetObject(), prefixes, prefix.size(), width, &count, typeCtx, context.size()); + data->GetObject(), addr, type->GetObject(), prefixes, prefix.size(), width, &count, typeCtx, context.size(), + language.c_str()); delete[] typeCtx; for (size_t i = 0; i < prefix.size(); i++) diff --git a/function.cpp b/function.cpp index 033a8453..33cb5e52 100644 --- a/function.cpp +++ b/function.cpp @@ -200,12 +200,12 @@ ConstantData::ConstantData(BNRegisterValueType _state, uint64_t _value, size_t _ } -DataBuffer ConstantData::ToDataBuffer() const +pair<DataBuffer, BNBuiltinType> ConstantData::ToDataBuffer() const { if (func) return func->GetConstantData(state, value, size); - return DataBuffer(); + return make_pair(DataBuffer(), BuiltinNone); } @@ -612,9 +612,11 @@ void PossibleValueSet::FreeAPIObject(BNPossibleValueSet* value) } -DataBuffer Function::GetConstantData(BNRegisterValueType state, uint64_t value, size_t size) +pair<DataBuffer, BNBuiltinType> Function::GetConstantData(BNRegisterValueType state, uint64_t value, size_t size) { - return DataBuffer(BNGetConstantData(m_object, state, value, size)); + BNBuiltinType builtin; + auto buffer = DataBuffer(BNGetConstantData(m_object, state, value, size, &builtin)); + return make_pair(buffer, builtin); } @@ -908,21 +910,22 @@ Ref<HighLevelILFunction> Function::GetHighLevelILIfAvailable() const } -Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentation() const +Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentation(const string& language) const { - BNLanguageRepresentationFunction* function = BNGetFunctionLanguageRepresentation(m_object); + BNLanguageRepresentationFunction* function = BNGetFunctionLanguageRepresentation(m_object, language.c_str()); if (!function) return nullptr; - return new LanguageRepresentationFunction(function); + return new CoreLanguageRepresentationFunction(function); } -Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentationIfAvailable() const +Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentationIfAvailable(const string& language) const { - BNLanguageRepresentationFunction* function = BNGetFunctionLanguageRepresentationIfAvailable(m_object); + BNLanguageRepresentationFunction* function = + BNGetFunctionLanguageRepresentationIfAvailable(m_object, language.c_str()); if (!function) return nullptr; - return new LanguageRepresentationFunction(function); + return new CoreLanguageRepresentationFunction(function); } @@ -1275,9 +1278,9 @@ void Function::ApplyAutoDiscoveredType(Type* type) } -Ref<FlowGraph> Function::CreateFunctionGraph(BNFunctionGraphType type, DisassemblySettings* settings) +Ref<FlowGraph> Function::CreateFunctionGraph(const FunctionViewType& type, DisassemblySettings* settings) { - BNFlowGraph* graph = BNCreateFunctionGraph(m_object, type, settings ? settings->GetObject() : nullptr); + BNFlowGraph* graph = BNCreateFunctionGraph(m_object, type.ToAPIObject(), settings ? settings->GetObject() : nullptr); return new CoreFlowGraph(graph); } diff --git a/highlevelil.cpp b/highlevelil.cpp index 7ac30f98..698b6119 100644 --- a/highlevelil.cpp +++ b/highlevelil.cpp @@ -341,6 +341,18 @@ bool HighLevelILFunction::IsVarLiveAt(const Variable& var, const size_t instr) c } +bool HighLevelILFunction::HasSideEffects(const HighLevelILInstruction& instr) +{ + return BNHighLevelILHasSideEffects(instr.function->GetObject(), instr.exprIndex); +} + + +BNScopeType HighLevelILFunction::GetExprScopeType(const HighLevelILInstruction& instr) +{ + return BNGetHighLevelILExprScopeType(instr.function->GetObject(), instr.exprIndex); +} + + set<size_t> HighLevelILFunction::GetVariableSSAVersions(const Variable& var) const { size_t count; @@ -568,3 +580,332 @@ set<size_t> HighLevelILFunction::GetUsesForLabel(uint64_t label) BNFreeILInstructionList(uses); return result; } + + +set<Variable> HighLevelILFunction::GetVariables() +{ + size_t count; + BNVariable* vars = BNGetHighLevelILVariables(m_object, &count); + + set<Variable> result; + for (size_t i = 0; i < count; ++i) + result.emplace(vars[i]); + + BNFreeVariableList(vars); + return result; +} + + +set<Variable> HighLevelILFunction::GetAliasedVariables() +{ + size_t count; + BNVariable* vars = BNGetHighLevelILAliasedVariables(m_object, &count); + + set<Variable> result; + for (size_t i = 0; i < count; ++i) + result.emplace(vars[i]); + + BNFreeVariableList(vars); + return result; +} + + +set<SSAVariable> HighLevelILFunction::GetSSAVariables() +{ + size_t count; + BNVariable* vars = BNGetHighLevelILVariables(m_object, &count); + + set<SSAVariable> result; + for (size_t i = 0; i < count; ++i) + { + size_t versionCount; + size_t* versions = BNGetHighLevelILVariableSSAVersions(m_object, &vars[i], &versionCount); + for (size_t j = 0; j < versionCount; ++j) + result.emplace(vars[i], versions[j]); + BNFreeILInstructionList(versions); + } + + BNFreeVariableList(vars); + return result; +} + + +HighLevelILTokenEmitter::CurrentExprGuard::CurrentExprGuard(HighLevelILTokenEmitter& parent, const BNTokenEmitterExpr& expr): + m_parent(&parent) +{ + m_expr = BNHighLevelILTokenEmitterSetCurrentExpr(m_parent->m_object, expr); +} + + +HighLevelILTokenEmitter::CurrentExprGuard::~CurrentExprGuard() +{ + BNHighLevelILTokenEmitterRestoreCurrentExpr(m_parent->m_object, m_expr); +} + + +HighLevelILTokenEmitter::HighLevelILTokenEmitter(BNHighLevelILTokenEmitter* emitter) +{ + m_object = emitter; +} + + +void HighLevelILTokenEmitter::NewLine() +{ + BNHighLevelILTokenEmitterNewLine(m_object); +} + + +void HighLevelILTokenEmitter::IncreaseIndent() +{ + BNHighLevelILTokenEmitterIncreaseIndent(m_object); +} + + +void HighLevelILTokenEmitter::DecreaseIndent() +{ + BNHighLevelILTokenEmitterDecreaseIndent(m_object); +} + + +void HighLevelILTokenEmitter::ScopeSeparator() +{ + BNHighLevelILTokenEmitterScopeSeparator(m_object); +} + + +void HighLevelILTokenEmitter::BeginScope(BNScopeType scopeType) +{ + BNHighLevelILTokenEmitterBeginScope(m_object, scopeType); +} + + +void HighLevelILTokenEmitter::EndScope(BNScopeType scopeType) +{ + BNHighLevelILTokenEmitterEndScope(m_object, scopeType); +} + + +void HighLevelILTokenEmitter::ScopeContinuation(bool forceSameLine) +{ + BNHighLevelILTokenEmitterScopeContinuation(m_object, forceSameLine); +} + + +void HighLevelILTokenEmitter::FinalizeScope() +{ + BNHighLevelILTokenEmitterFinalizeScope(m_object); +} + + +void HighLevelILTokenEmitter::NoIndentForThisLine() +{ + BNHighLevelILTokenEmitterNoIndentForThisLine(m_object); +} + + +void HighLevelILTokenEmitter::BeginForceZeroConfidence() +{ + BNHighLevelILTokenEmitterBeginForceZeroConfidence(m_object); +} + + +void HighLevelILTokenEmitter::EndForceZeroConfidence() +{ + BNHighLevelILTokenEmitterEndForceZeroConfidence(m_object); +} + + +HighLevelILTokenEmitter::CurrentExprGuard HighLevelILTokenEmitter::SetCurrentExpr(const HighLevelILInstruction& expr) +{ + return CurrentExprGuard(*this, {expr.address, expr.sourceOperand, expr.exprIndex, expr.instructionIndex}); +} + + +void HighLevelILTokenEmitter::Finalize() +{ + BNHighLevelILTokenEmitterFinalize(m_object); +} + + +void HighLevelILTokenEmitter::AppendOpenParen() +{ + BNHighLevelILTokenEmitterAppendOpenParen(m_object); +} + + +void HighLevelILTokenEmitter::AppendCloseParen() +{ + BNHighLevelILTokenEmitterAppendCloseParen(m_object); +} + + +void HighLevelILTokenEmitter::AppendOpenBracket() +{ + BNHighLevelILTokenEmitterAppendOpenBracket(m_object); +} + + +void HighLevelILTokenEmitter::AppendCloseBracket() +{ + BNHighLevelILTokenEmitterAppendCloseBracket(m_object); +} + + +void HighLevelILTokenEmitter::AppendOpenBrace() +{ + BNHighLevelILTokenEmitterAppendOpenBrace(m_object); +} + + +void HighLevelILTokenEmitter::AppendCloseBrace() +{ + BNHighLevelILTokenEmitterAppendCloseBrace(m_object); +} + + +void HighLevelILTokenEmitter::AppendSemicolon() +{ + BNHighLevelILTokenEmitterAppendSemicolon(m_object); +} + + +vector<InstructionTextToken> HighLevelILTokenEmitter::GetCurrentTokens() const +{ + size_t count = 0; + BNInstructionTextToken* tokens = BNHighLevelILTokenEmitterGetCurrentTokens(m_object, &count); + return InstructionTextToken::ConvertAndFreeInstructionTextTokenList(tokens, count); +} + + +void HighLevelILTokenEmitter::SetBraceRequirement(BNBraceRequirement required) +{ + BNHighLevelILTokenEmitterSetBraceRequirement(m_object, required); +} + + +void HighLevelILTokenEmitter::SetBracesAroundSwitchCases(bool braces) +{ + BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(m_object, braces); +} + + +void HighLevelILTokenEmitter::SetDefaultBracesOnSameLine(bool sameLine) +{ + BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(m_object, sameLine); +} + + +void HighLevelILTokenEmitter::SetSimpleScopeAllowed(bool allowed) +{ + BNHighLevelILTokenEmitterSetSimpleScopeAllowed(m_object, allowed); +} + + +BNBraceRequirement HighLevelILTokenEmitter::GetBraceRequirement() const +{ + return BNHighLevelILTokenEmitterGetBraceRequirement(m_object); +} + + +bool HighLevelILTokenEmitter::HasBracesAroundSwitchCases() const +{ + return BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(m_object); +} + + +bool HighLevelILTokenEmitter::GetDefaultBracesOnSameLine() const +{ + return BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(m_object); +} + + +bool HighLevelILTokenEmitter::IsSimpleScopeAllowed() const +{ + return BNHighLevelILTokenEmitterIsSimpleScopeAllowed(m_object); +} + + +vector<DisassemblyTextLine> HighLevelILTokenEmitter::GetLines() const +{ + size_t count = 0; + BNDisassemblyTextLine* lines = BNHighLevelILTokenEmitterGetLines(m_object, &count); + + vector<DisassemblyTextLine> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + DisassemblyTextLine line; + line.addr = lines[i].addr; + line.instrIndex = lines[i].instrIndex; + line.highlight = lines[i].highlight; + line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count); + line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount); + result.push_back(line); + } + + BNFreeDisassemblyTextLines(lines, count); + return result; +} + + +void HighLevelILTokenEmitter::AppendSizeToken(size_t size, BNInstructionTextTokenType type) +{ + BNAddHighLevelILSizeToken(size, type, m_object); +} + + +void HighLevelILTokenEmitter::AppendFloatSizeToken(size_t size, BNInstructionTextTokenType type) +{ + BNAddHighLevelILFloatSizeToken(size, type, m_object); +} + + +void HighLevelILTokenEmitter::AppendVarTextToken(const Variable& var, const HighLevelILInstruction& instr, size_t size) +{ + BNAddHighLevelILVarTextToken(instr.function->GetObject(), &var, m_object, instr.exprIndex, size); +} + + +void HighLevelILTokenEmitter::AppendIntegerTextToken(const HighLevelILInstruction& instr, int64_t val, size_t size) +{ + BNAddHighLevelILIntegerTextToken(instr.function->GetObject(), instr.exprIndex, val, size, m_object); +} + + +void HighLevelILTokenEmitter::AppendArrayIndexToken( + const HighLevelILInstruction& instr, int64_t val, size_t size, uint64_t address) +{ + BNAddHighLevelILArrayIndexToken(instr.function->GetObject(), instr.exprIndex, val, size, m_object, address); +} + + +BNSymbolDisplayResult HighLevelILTokenEmitter::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val, + DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence, + bool allowShortString) +{ + return BNAddHighLevelILPointerTextToken(instr.function->GetObject(), instr.exprIndex, val, m_object, + settings ? settings->GetObject() : nullptr, symbolDisplay, precedence, allowShortString); +} + + +void HighLevelILTokenEmitter::AppendConstantTextToken(const HighLevelILInstruction& instr, int64_t val, size_t size, + DisassemblySettings* settings, BNOperatorPrecedence precedence) +{ + BNAddHighLevelILConstantTextToken(instr.function->GetObject(), instr.exprIndex, val, size, m_object, + settings ? settings->GetObject() : nullptr, precedence); +} + + +void HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( + BinaryView* data, Type* type, const HighLevelILInstruction& var, vector<string>& nameList) +{ + size_t nameCount = 0; + char** names = BNAddNamesForOuterStructureMembers( + data->GetObject(), type->GetObject(), var.function->GetObject(), var.exprIndex, &nameCount); + vector<string> newNames; + newNames.reserve(nameCount); + for (size_t i = 0; i < nameCount; i++) + newNames.push_back(names[i]); + BNFreeStringList(names, nameCount); + nameList.insert(nameList.begin(), newNames.begin(), newNames.end()); +} diff --git a/lang/c/CMakeLists.txt b/lang/c/CMakeLists.txt new file mode 100644 index 00000000..9ccc89f2 --- /dev/null +++ b/lang/c/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(lang_pseudoc) + +if(NOT BN_INTERNAL_BUILD) + add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api) +endif() + +file(GLOB SOURCES + *.cpp + *.h) + +if(DEMO) + add_library(lang_pseudoc STATIC ${SOURCES}) +else() + add_library(lang_pseudoc SHARED ${SOURCES}) +endif() + +target_include_directories(lang_pseudoc + PRIVATE ${PROJECT_SOURCE_DIR}) + +if(WIN32) + target_link_directories(lang_pseudoc + PRIVATE ${BN_INSTALL_DIR}) + target_link_libraries(lang_pseudoc binaryninjaapi binaryninjacore) +else() + target_link_libraries(lang_pseudoc binaryninjaapi) +endif() + +set_target_properties(lang_pseudoc PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + C_STANDARD 99 + C_STANDARD_REQUIRED ON + C_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON) + +if(BN_INTERNAL_BUILD) + plugin_rpath(lang_pseudoc) + set_target_properties(lang_pseudoc PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +endif() diff --git a/lang/c/pseudoc.cpp b/lang/c/pseudoc.cpp new file mode 100644 index 00000000..b2dca522 --- /dev/null +++ b/lang/c/pseudoc.cpp @@ -0,0 +1,2852 @@ +#include <inttypes.h> +#include "pseudoc.h" +#include "highlevelilinstruction.h" + +using namespace std; +using namespace BinaryNinja; + + +PseudoCFunction::PseudoCFunction( + Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) : + LanguageRepresentationFunction(arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction) +{ +} + + +void PseudoCFunction::InitTokenEmitter(HighLevelILTokenEmitter& tokens) +{ + // Braces must always be turned on for C + tokens.SetBraceRequirement(BracesAlwaysRequired); + + // For switch cases in C, declarations inside aren't scoped by default. Add braces around the cases + // to give them scope. + tokens.SetBracesAroundSwitchCases(true); +} + + +void PseudoCFunction::BeginLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens) +{ + if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex) + { + // At top level, add braces around the entire function + tokens.AppendOpenBrace(); + tokens.NewLine(); + tokens.IncreaseIndent(); + } +} + + +void PseudoCFunction::EndLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens) +{ + if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex) + { + // At top level, add braces around the entire function + tokens.NewLine(); + tokens.DecreaseIndent(); + tokens.AppendCloseBrace(); + } +} + + +BNSymbolDisplayResult PseudoCFunction::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val, + vector<InstructionTextToken>& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence) +{ + Confidence<Ref<Type>> type = instr.GetType(); + if (type && (type->GetClass() == PointerTypeClass) && type->IsConst()) + { + string stringValue; + size_t childWidth = 0; + if (auto child = type->GetChildType(); child) + childWidth = child->GetWidth(); + if (auto strType = GetFunction()->GetView()->CheckForStringAnnotationType(val, stringValue, false, false, childWidth); strType.has_value()) + { + if (symbolDisplay == DereferenceNonDataSymbols) + { + if (precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, "("); + tokens.emplace_back(OperationToken, "*"); + } + tokens.emplace_back(BraceToken, DisassemblyTextRenderer::GetStringLiteralPrefix(strType.value()) + string("\"")); + tokens.emplace_back(StringToken, StringReferenceTokenContext, stringValue, instr.address, strType.value()); + tokens.emplace_back(BraceToken, "\""); + if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, ")"); + return OtherSymbolResult; + } + } + + if (GetFunction()) + { + // If the pointer has a value of 0, check if it points to a valid address by + // 1. If the binary is relocatable, assign the pointer as nullptr + // 2. else, check if the constant zero which being referenced is a pointer(display as symbol) or not(display as nullptr) + if(val == 0x0 && type && (type->GetClass() == PointerTypeClass)) + { + if (GetFunction()->GetView()->IsRelocatable()) + { + if (symbolDisplay == DereferenceNonDataSymbols) + { + if (precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, "("); + tokens.emplace_back(OperationToken, "*"); + } + tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val); + if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, ")"); + return OtherSymbolResult; + } + + auto arch = GetHighLevelILFunction()->GetArchitecture(); + auto refs = GetHighLevelILFunction()->GetFunction()->GetConstantsReferencedByInstructionIfAvailable( + arch, instr.address); + bool constantZeroBeingReferencedIsPointer = false; + + for (const BNConstantReference& ref : refs) + if (ref.value == 0x0 && ref.pointer) + constantZeroBeingReferencedIsPointer = true; + if (!constantZeroBeingReferencedIsPointer) + { + if (symbolDisplay == DereferenceNonDataSymbols) + { + if (precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, "("); + tokens.emplace_back(OperationToken, "*"); + } + tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val); + if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, ")"); + return OtherSymbolResult; + } + } + + Ref<BinaryView> data = GetFunction()->GetView(); + vector<InstructionTextToken> symTokens; + BNSymbolDisplayResult result = DisassemblyTextRenderer::AddSymbolTokenStatic(symTokens, val, 0, + BN_INVALID_OPERAND, data, settings ? settings->GetMaximumSymbolWidth() : 0, GetFunction(), + BN_FULL_CONFIDENCE, symbolDisplay, precedence, instr.address); + if (result != NoSymbolAvailable) + { + for (auto& i : symTokens) + tokens.emplace_back(i); + return result; + } + } + + if (symbolDisplay == DereferenceNonDataSymbols) + { + if (precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, "("); + + tokens.emplace_back(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.emplace_back(BraceToken, "("); + tokens.emplace_back(TypeNameToken, GetSizeToken(instr.size, false)); + tokens.emplace_back(OperationToken, "*"); + tokens.emplace_back(BraceToken, ")"); + } + } + + char valStr[32]; + if (val >= 0) + { + if (val <= 9) + snprintf(valStr, sizeof(valStr), "%" PRIx64, val); + else + snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val); + } + else + { + if (val >= -9) + snprintf(valStr, sizeof(valStr), "-%" PRIx64, val); + else + snprintf(valStr, sizeof(valStr), "-0x%" PRIx64, val); + } + + tokens.emplace_back(PossibleAddressToken, InstructionAddressTokenContext, valStr, instr.address, val); + + if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, ")"); + return OtherSymbolResult; +} + + +string PseudoCFunction::GetSizeToken(size_t size, bool isSigned) +{ + char sizeStr[32]; + + switch (size) + { + case 0: + return {}; + case 1: + return (isSigned ? "int8_t" : "uint8_t"); + case 2: + return (isSigned ? "int16_t" : "uint16_t"); + case 4: + return (isSigned ? "int32_t" : "uint32_t"); + case 8: + return (isSigned ? "int64_t" : "uint64_t"); + case 10: + return (isSigned ? "int80_t" : "uint80_t"); + case 16: + return (isSigned ? "int128_t" : "uint128_t"); + } + + snprintf(sizeStr, sizeof(sizeStr), "%sint%" PRIuPTR "_t", isSigned ? "" : "u", size); + return {sizeStr}; +} + + +void PseudoCFunction::AppendSizeToken(size_t size, bool isSigned, HighLevelILTokenEmitter& emitter) +{ + const auto token = GetSizeToken(size, isSigned); + if (!token.empty()) + emitter.Append(TypeNameToken, token); +} + + +void PseudoCFunction::AppendSingleSizeToken( + size_t size, BNInstructionTextTokenType type, HighLevelILTokenEmitter& emitter) +{ + char sizeStr[32]; + + switch (size) + { + case 0: + break; + case 1: + emitter.Append(type, "B"); + break; + case 2: + emitter.Append(type, "W"); + break; + case 4: + emitter.Append(type, "D"); + break; + case 8: + emitter.Append(type, "Q"); + break; + case 10: + emitter.Append(type, "T"); + break; + case 16: + emitter.Append(type, "O"); + break; + default: + snprintf(sizeStr, sizeof(sizeStr), "%" PRIuPTR "", size); + emitter.Append(type, sizeStr); + break; + } +} + + +void PseudoCFunction::AppendComparison(const string& comparison, const HighLevelILInstruction& instr, + HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, + std::optional<bool> signedHint) +{ + const auto leftExpr = instr.GetLeftExpr(); + const auto rightExpr = instr.GetRightExpr(); + + GetExprTextInternal(leftExpr, emitter, settings, asFullAst, precedence, false, signedHint); + emitter.Append(OperationToken, comparison); + GetExprTextInternal(rightExpr, emitter, settings, asFullAst, precedence, false, signedHint); +} + + +void PseudoCFunction::AppendTwoOperand(const string& operand, const HighLevelILInstruction& instr, + HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, + std::optional<bool> signedHint) +{ + const auto& twoOperand = instr.AsTwoOperand(); + const auto leftExpr = twoOperand.GetLeftExpr(); + const auto rightExpr = twoOperand.GetRightExpr(); + BNOperatorPrecedence leftPrecedence = precedence; + switch (precedence) + { + case SubOperatorPrecedence: + // Treat left side of subtraction as same level as addition. This lets + // (a - b) - c be represented as a - b - c, but a - (b - c) does not + // simplify at rendering + leftPrecedence = AddOperatorPrecedence; + break; + case DivideOperatorPrecedence: + // Treat left side of divison as same level as multiplication. This lets + // (a / b) / c be represented as a / b / c, but a / (b / c) does not + // simplify at rendering + leftPrecedence = MultiplyOperatorPrecedence; + break; + default: + break; + } + + if (leftExpr.operation == HLIL_SPLIT) + { + const auto low = leftExpr.GetLowExpr(); + const auto high = leftExpr.GetHighExpr(); + + emitter.Append(OperationToken, "COMBINE"); + emitter.AppendOpenParen(); + GetExprTextInternal(high, emitter, settings, asFullAst); + emitter.Append(TextToken, ", "); + GetExprTextInternal(low, emitter, settings, asFullAst); + emitter.AppendCloseParen(); + } + + + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + if (leftExpr.operation == HLIL_VAR && (operand == " + " || operand == " - ")) + { + const auto variableType = GetFunction()->GetVariableType(leftExpr.GetVariable()); + if (variableType) + { + const auto childType = variableType->GetChildType(); + if (variableType->IsPointer() && childType && childType->GetWidth() != 1) + { + emitter.AppendOpenParen(); + emitter.Append(TypeNameToken, "char"); + emitter.Append(TextToken, "*"); + emitter.AppendCloseParen(); + } + } + } + } + + GetExprTextInternal(leftExpr, emitter, settings, asFullAst, leftPrecedence, false, signedHint); + + auto lessThanZero = [](uint64_t value, uint64_t width) -> bool { + return ((1UL << ((width * 8) - 1UL)) & value) != 0; + }; + + if ((operand == " + ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size) && + rightExpr.size >= leftExpr.size) + { + // Convert addition of a negative constant into subtraction of a positive constant + emitter.Append(OperationToken, " - "); + emitter.AppendIntegerTextToken( + rightExpr, -BNSignExtend(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size); + return; + } + if ((operand == " - ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size) && + rightExpr.size >= leftExpr.size) + { + // Convert subtraction of a negative constant into addition of a positive constant + emitter.Append(OperationToken, " + "); + emitter.AppendIntegerTextToken( + rightExpr, -BNSignExtend(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size); + return; + } + + emitter.Append(OperationToken, operand); + GetExprTextInternal(rightExpr, emitter, settings, asFullAst, precedence, false, signedHint); +} + + +void PseudoCFunction::AppendTwoOperandFunction(const string& function, + const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, + bool sizeToken) +{ + const auto& twoOperand = instr.AsTwoOperand(); + const auto leftExpr = twoOperand.GetLeftExpr(); + const auto rightExpr = twoOperand.GetRightExpr(); + + emitter.Append(OperationToken, function); + if (sizeToken) + AppendSingleSizeToken(twoOperand.size, OperationToken, emitter); + emitter.AppendOpenParen(); + + if (leftExpr.operation == HLIL_SPLIT) + { + const auto low = leftExpr.GetLowExpr(); + const auto high = leftExpr.GetHighExpr(); + + emitter.Append(OperationToken, "COMBINE"); + emitter.AppendOpenParen(); + GetExprTextInternal(high, emitter, settings); + emitter.Append(TextToken, ", "); + GetExprTextInternal(low, emitter, settings); + emitter.AppendCloseParen(); + } + else + { + GetExprTextInternal(leftExpr, emitter, settings); + } + + emitter.Append(TextToken, ", "); + GetExprTextInternal(rightExpr, emitter, settings); + + emitter.AppendCloseParen(); +} + + +void PseudoCFunction::AppendTwoOperandFunctionWithCarry(const string& function, + const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings) +{ + const auto leftExpr = instr.GetLeftExpr(); + const auto rightExpr = instr.GetRightExpr(); + const auto carryExpr = instr.GetCarryExpr(); + + tokens.Append(OperationToken, function); + AppendSingleSizeToken(instr.size, OperationToken, tokens); + tokens.AppendOpenParen(); + + if (leftExpr.operation == HLIL_SPLIT) + { + const auto low = leftExpr.GetLowExpr(); + const auto high = leftExpr.GetHighExpr(); + + tokens.Append(OperationToken, "COMBINE"); + tokens.AppendOpenParen(); + GetExprTextInternal(high, tokens, settings); + tokens.Append(TextToken, ", "); + GetExprTextInternal(low, tokens, settings); + tokens.AppendCloseParen(); + } + + GetExprTextInternal(leftExpr, tokens, settings); + tokens.Append(TextToken, ", "); + GetExprTextInternal(rightExpr, tokens, settings); + tokens.Append(TextToken, ", "); + GetExprTextInternal(carryExpr, tokens, settings); + + tokens.AppendCloseParen(); +} + + +Ref<Type> PseudoCFunction::GetFieldType(const HighLevelILInstruction& var, bool deref) +{ + Ref<Type> type = var.GetType().GetValue(); + if (deref && type && (type->GetClass() == PointerTypeClass)) + type = type->GetChildType().GetValue(); + + if (type && (type->GetClass() == NamedTypeReferenceClass)) + type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference()); + + return type; +} + + +PseudoCFunction::FieldDisplayType PseudoCFunction::GetFieldDisplayType( + Ref<Type> type, uint64_t offset, size_t memberIndex, bool deref) +{ + if (type && (type->GetClass() == StructureTypeClass)) + { + std::optional<size_t> memberIndexHint; + if (memberIndex != BN_INVALID_EXPR) + memberIndexHint = memberIndex; + + if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0, + [&](NamedTypeReference*, Structure*, size_t, uint64_t, uint64_t, const StructureMember&) {}), + memberIndexHint) + return FieldDisplayName; + return FieldDisplayOffset; + } + else if (deref || offset != 0) + return FieldDisplayMemberOffset; + else + return FieldDisplayNone; +} + + +void PseudoCFunction::AppendFieldTextTokens(const HighLevelILInstruction& var, uint64_t offset, + size_t memberIndex, size_t size, HighLevelILTokenEmitter& tokens, bool deref, bool displayDeref) +{ + const auto type = GetFieldType(var, deref); + const auto fieldDisplayType = GetFieldDisplayType(type, offset, memberIndex, deref); + switch (fieldDisplayType) + { + case FieldDisplayName: + { + std::optional<size_t> memberIndexHint; + if (memberIndex != BN_INVALID_EXPR) + memberIndexHint = memberIndex; + + if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0, + [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset, + uint64_t adjustedOffset, const StructureMember& member) { + if (deref && displayDeref) + tokens.Append(OperationToken, "->"); + else + tokens.Append(OperationToken, "."); + deref = false; + + vector<string> nameList {member.name}; + HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( + GetFunction()->GetView(), type, var, nameList); + + tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0, + BN_FULL_CONFIDENCE, nameList); + }), + memberIndexHint) + return; + + // Part of structure but no defined field, use __offset syntax + if (deref && displayDeref) + tokens.Append(OperationToken, "->"); + else + tokens.Append(OperationToken, "."); + char offsetStr[64]; + snprintf( + offsetStr, sizeof(offsetStr), "__offset(0x%" PRIx64 ")%s", offset, Type::GetSizeSuffix(size).c_str()); + + vector<string> nameList {offsetStr}; + HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(GetFunction()->GetView(), type, var, nameList); + + tokens.Append(StructOffsetToken, offsetStr, offset, size, 0, BN_FULL_CONFIDENCE, nameList); + return; + } + + case FieldDisplayOffset: + { + /* this is handled before the display */ + return; + } + + case FieldDisplayMemberOffset: + { + tokens.AppendOpenBracket(); + tokens.AppendIntegerTextToken(var, offset, size); + tokens.AppendCloseBracket(); + return; + } + + default: break; + } +} + + +void PseudoCFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, + DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement) +{ + GetExprTextInternal(instr, tokens, settings, asFullAst, precedence, statement); +} + + +void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, + DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement, + optional<bool> signedHint) +{ + // The lambdas in this function are here to reduce stack frame size of this function. Without them, + // complex expression can cause the process to crash from a stack overflow. + auto exprGuard = tokens.SetCurrentExpr(instr); + + if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType()) + { + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + for (auto& token: instr.GetType()->GetTokens(GetArchitecture()->GetStandalonePlatform())) + { + tokens.Append(token); + } + tokens.AppendCloseParen(); + tokens.Append(TextToken, " "); + } + if (settings && settings->IsOptionSet(ShowILOpcodes)) + { + tokens.Append(OperationToken, "/*"); + switch (instr.operation) + { + case HLIL_NOP: tokens.Append(OperationToken, "HLIL_NOP"); break; + case HLIL_BLOCK: tokens.Append(OperationToken, "HLIL_BLOCK"); break; + case HLIL_IF: tokens.Append(OperationToken, "HLIL_IF"); break; + case HLIL_WHILE: tokens.Append(OperationToken, "HLIL_WHILE"); break; + case HLIL_DO_WHILE: tokens.Append(OperationToken, "HLIL_DO_WHILE"); break; + case HLIL_FOR: tokens.Append(OperationToken, "HLIL_FOR"); break; + case HLIL_SWITCH: tokens.Append(OperationToken, "HLIL_SWITCH"); break; + case HLIL_CASE: tokens.Append(OperationToken, "HLIL_CASE"); break; + case HLIL_BREAK: tokens.Append(OperationToken, "HLIL_BREAK"); break; + case HLIL_CONTINUE: tokens.Append(OperationToken, "HLIL_CONTINUE"); break; + case HLIL_JUMP: tokens.Append(OperationToken, "HLIL_JUMP"); break; + case HLIL_RET: tokens.Append(OperationToken, "HLIL_RET"); break; + case HLIL_NORET: tokens.Append(OperationToken, "HLIL_NORET"); break; + case HLIL_GOTO: tokens.Append(OperationToken, "HLIL_GOTO"); break; + case HLIL_LABEL: tokens.Append(OperationToken, "HLIL_LABEL"); break; + case HLIL_VAR_DECLARE: tokens.Append(OperationToken, "HLIL_VAR_DECLARE"); break; + case HLIL_VAR_INIT: tokens.Append(OperationToken, "HLIL_VAR_INIT"); break; + case HLIL_ASSIGN: tokens.Append(OperationToken, "HLIL_ASSIGN"); break; + case HLIL_ASSIGN_UNPACK: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK"); break; + case HLIL_VAR: tokens.Append(OperationToken, "HLIL_VAR"); break; + case HLIL_STRUCT_FIELD: tokens.Append(OperationToken, "HLIL_STRUCT_FIELD"); break; + case HLIL_ARRAY_INDEX: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX"); break; + case HLIL_SPLIT: tokens.Append(OperationToken, "HLIL_SPLIT"); break; + case HLIL_DEREF: tokens.Append(OperationToken, "HLIL_DEREF"); break; + case HLIL_DEREF_FIELD: tokens.Append(OperationToken, "HLIL_DEREF_FIELD"); break; + case HLIL_ADDRESS_OF: tokens.Append(OperationToken, "HLIL_ADDRESS_OF"); break; + case HLIL_CONST: tokens.Append(OperationToken, "HLIL_CONST"); break; + case HLIL_CONST_DATA: tokens.Append(OperationToken, "HLIL_CONST_DATA"); break; + case HLIL_CONST_PTR: tokens.Append(OperationToken, "HLIL_CONST_PTR"); break; + case HLIL_EXTERN_PTR: tokens.Append(OperationToken, "HLIL_EXTERN_PTR"); break; + case HLIL_FLOAT_CONST: tokens.Append(OperationToken, "HLIL_FLOAT_CONST"); break; + case HLIL_IMPORT: tokens.Append(OperationToken, "HLIL_IMPORT"); break; + case HLIL_ADD: tokens.Append(OperationToken, "HLIL_ADD"); break; + case HLIL_ADC: tokens.Append(OperationToken, "HLIL_ADC"); break; + case HLIL_SUB: tokens.Append(OperationToken, "HLIL_SUB"); break; + case HLIL_SBB: tokens.Append(OperationToken, "HLIL_SBB"); break; + case HLIL_AND: tokens.Append(OperationToken, "HLIL_AND"); break; + case HLIL_OR: tokens.Append(OperationToken, "HLIL_OR"); break; + case HLIL_XOR: tokens.Append(OperationToken, "HLIL_XOR"); break; + case HLIL_LSL: tokens.Append(OperationToken, "HLIL_LSL"); break; + case HLIL_LSR: tokens.Append(OperationToken, "HLIL_LSR"); break; + case HLIL_ASR: tokens.Append(OperationToken, "HLIL_ASR"); break; + case HLIL_ROL: tokens.Append(OperationToken, "HLIL_ROL"); break; + case HLIL_RLC: tokens.Append(OperationToken, "HLIL_RLC"); break; + case HLIL_ROR: tokens.Append(OperationToken, "HLIL_ROR"); break; + case HLIL_RRC: tokens.Append(OperationToken, "HLIL_RRC"); break; + case HLIL_MUL: tokens.Append(OperationToken, "HLIL_MUL"); break; + case HLIL_MULU_DP: tokens.Append(OperationToken, "HLIL_MULU_DP"); break; + case HLIL_MULS_DP: tokens.Append(OperationToken, "HLIL_MULS_DP"); break; + case HLIL_DIVU: tokens.Append(OperationToken, "HLIL_DIVU"); break; + case HLIL_DIVU_DP: tokens.Append(OperationToken, "HLIL_DIVU_DP"); break; + case HLIL_DIVS: tokens.Append(OperationToken, "HLIL_DIVS"); break; + case HLIL_DIVS_DP: tokens.Append(OperationToken, "HLIL_DIVS_DP"); break; + case HLIL_MODU: tokens.Append(OperationToken, "HLIL_MODU"); break; + case HLIL_MODU_DP: tokens.Append(OperationToken, "HLIL_MODU_DP"); break; + case HLIL_MODS: tokens.Append(OperationToken, "HLIL_MODS"); break; + case HLIL_MODS_DP: tokens.Append(OperationToken, "HLIL_MODS_DP"); break; + case HLIL_NEG: tokens.Append(OperationToken, "HLIL_NEG"); break; + case HLIL_NOT: tokens.Append(OperationToken, "HLIL_NOT"); break; + case HLIL_SX: tokens.Append(OperationToken, "HLIL_SX"); break; + case HLIL_ZX: tokens.Append(OperationToken, "HLIL_ZX"); break; + case HLIL_LOW_PART: tokens.Append(OperationToken, "HLIL_LOW_PART"); break; + case HLIL_CALL: tokens.Append(OperationToken, "HLIL_CALL"); break; + case HLIL_CMP_E: tokens.Append(OperationToken, "HLIL_CMP_E"); break; + case HLIL_CMP_NE: tokens.Append(OperationToken, "HLIL_CMP_NE"); break; + case HLIL_CMP_SLT: tokens.Append(OperationToken, "HLIL_CMP_SLT"); break; + case HLIL_CMP_ULT: tokens.Append(OperationToken, "HLIL_CMP_ULT"); break; + case HLIL_CMP_SLE: tokens.Append(OperationToken, "HLIL_CMP_SLE"); break; + case HLIL_CMP_ULE: tokens.Append(OperationToken, "HLIL_CMP_ULE"); break; + case HLIL_CMP_SGE: tokens.Append(OperationToken, "HLIL_CMP_SGE"); break; + case HLIL_CMP_UGE: tokens.Append(OperationToken, "HLIL_CMP_UGE"); break; + case HLIL_CMP_SGT: tokens.Append(OperationToken, "HLIL_CMP_SGT"); break; + case HLIL_CMP_UGT: tokens.Append(OperationToken, "HLIL_CMP_UGT"); break; + case HLIL_TEST_BIT: tokens.Append(OperationToken, "HLIL_TEST_BIT"); break; + case HLIL_BOOL_TO_INT: tokens.Append(OperationToken, "HLIL_BOOL_TO_INT"); break; + case HLIL_ADD_OVERFLOW: tokens.Append(OperationToken, "HLIL_ADD_OVERFLOW"); break; + case HLIL_SYSCALL: tokens.Append(OperationToken, "HLIL_SYSCALL"); break; + case HLIL_TAILCALL: tokens.Append(OperationToken, "HLIL_TAILCALL"); break; + case HLIL_INTRINSIC: tokens.Append(OperationToken, "HLIL_INTRINSIC"); break; + case HLIL_BP: tokens.Append(OperationToken, "HLIL_BP"); break; + case HLIL_TRAP: tokens.Append(OperationToken, "HLIL_TRAP"); break; + case HLIL_UNDEF: tokens.Append(OperationToken, "HLIL_UNDEF"); break; + case HLIL_UNIMPL: tokens.Append(OperationToken, "HLIL_UNIMPL"); break; + case HLIL_UNIMPL_MEM: tokens.Append(OperationToken, "HLIL_UNIMPL_MEM"); break; + case HLIL_FADD: tokens.Append(OperationToken, "HLIL_FADD"); break; + case HLIL_FSUB: tokens.Append(OperationToken, "HLIL_FSUB"); break; + case HLIL_FMUL: tokens.Append(OperationToken, "HLIL_FMUL"); break; + case HLIL_FDIV: tokens.Append(OperationToken, "HLIL_FDIV"); break; + case HLIL_FSQRT: tokens.Append(OperationToken, "HLIL_FSQRT"); break; + case HLIL_FNEG: tokens.Append(OperationToken, "HLIL_FNEG"); break; + case HLIL_FABS: tokens.Append(OperationToken, "HLIL_FABS"); break; + case HLIL_FLOAT_TO_INT: tokens.Append(OperationToken, "HLIL_FLOAT_TO_INT"); break; + case HLIL_INT_TO_FLOAT: tokens.Append(OperationToken, "HLIL_INT_TO_FLOAT"); break; + case HLIL_FLOAT_CONV: tokens.Append(OperationToken, "HLIL_FLOAT_CONV"); break; + case HLIL_ROUND_TO_INT: tokens.Append(OperationToken, "HLIL_ROUND_TO_INT"); break; + case HLIL_FLOOR: tokens.Append(OperationToken, "HLIL_FLOOR"); break; + case HLIL_CEIL: tokens.Append(OperationToken, "HLIL_CEIL"); break; + case HLIL_FTRUNC: tokens.Append(OperationToken, "HLIL_FTRUNC"); break; + case HLIL_FCMP_E: tokens.Append(OperationToken, "HLIL_FCMP_E"); break; + case HLIL_FCMP_NE: tokens.Append(OperationToken, "HLIL_FCMP_NE"); break; + case HLIL_FCMP_LT: tokens.Append(OperationToken, "HLIL_FCMP_LT"); break; + case HLIL_FCMP_LE: tokens.Append(OperationToken, "HLIL_FCMP_LE"); break; + case HLIL_FCMP_GE: tokens.Append(OperationToken, "HLIL_FCMP_GE"); break; + case HLIL_FCMP_GT: tokens.Append(OperationToken, "HLIL_FCMP_GT"); break; + case HLIL_FCMP_O: tokens.Append(OperationToken, "HLIL_FCMP_O"); break; + case HLIL_FCMP_UO: tokens.Append(OperationToken, "HLIL_FCMP_UO"); break; + case HLIL_UNREACHABLE: tokens.Append(OperationToken, "HLIL_UNREACHABLE"); break; + case HLIL_WHILE_SSA: tokens.Append(OperationToken, "HLIL_WHILE_SSA"); break; + case HLIL_DO_WHILE_SSA: tokens.Append(OperationToken, "HLIL_DO_WHILE_SSA"); break; + case HLIL_FOR_SSA: tokens.Append(OperationToken, "HLIL_FOR_SSA"); break; + case HLIL_VAR_INIT_SSA: tokens.Append(OperationToken, "HLIL_VAR_INIT_SSA"); break; + case HLIL_ASSIGN_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_MEM_SSA"); break; + case HLIL_ASSIGN_UNPACK_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK_MEM_SSA"); break; + case HLIL_VAR_SSA: tokens.Append(OperationToken, "HLIL_VAR_SSA"); break; + case HLIL_ARRAY_INDEX_SSA: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX_SSA"); break; + case HLIL_DEREF_SSA: tokens.Append(OperationToken, "HLIL_DEREF_SSA"); break; + case HLIL_DEREF_FIELD_SSA: tokens.Append(OperationToken, "HLIL_DEREF_FIELD_SSA"); break; + case HLIL_CALL_SSA: tokens.Append(OperationToken, "HLIL_CALL_SSA"); break; + case HLIL_SYSCALL_SSA: tokens.Append(OperationToken, "HLIL_SYSCALL_SSA"); break; + case HLIL_INTRINSIC_SSA: tokens.Append(OperationToken, "HLIL_INTRINSIC_SSA"); break; + case HLIL_VAR_PHI: tokens.Append(OperationToken, "HLIL_VAR_PHI"); break; + case HLIL_MEM_PHI: tokens.Append(OperationToken, "HLIL_MEM_PHI"); break; + } + tokens.Append(OperationToken, "*/"); + tokens.Append(TextToken, " "); + } + + switch (instr.operation) + { + case HLIL_BLOCK: + [&]() { + const auto exprs = instr.GetBlockExprs<HLIL_BLOCK>(); + bool needSeparator = false; + for (auto i = exprs.begin(); i != exprs.end(); ++i) + { + // Don't show void returns at the very end of the function when printing + // the root of an AST, as it is implicit and almost always omitted in + // normal source code. + auto next = i; + ++next; + if (asFullAst && (instr.exprIndex == GetHighLevelILFunction()->GetRootExpr().exprIndex) && (exprs.size() > 1) && + (next == exprs.end()) && ((*i).operation == HLIL_RET) && + ((*i).GetSourceExprs<HLIL_RET>().size() == 0)) + continue; + + // If the statement is one that contains additional blocks of code, insert a scope separator + // to visually separate the logic. + bool hasBlocks = false; + switch ((*i).operation) + { + case HLIL_IF: + case HLIL_WHILE: + case HLIL_WHILE_SSA: + case HLIL_DO_WHILE: + case HLIL_DO_WHILE_SSA: + case HLIL_FOR: + case HLIL_FOR_SSA: + case HLIL_SWITCH: + hasBlocks = true; + break; + default: + hasBlocks = false; + break; + } + if (needSeparator || (i != exprs.begin() && hasBlocks)) + { + tokens.ScopeSeparator(); + } + needSeparator = hasBlocks; + + // Emit the lines for the statement itself + GetExprTextInternal(*i, tokens, settings, true, TopLevelOperatorPrecedence, true); + tokens.NewLine(); + } + }(); + break; + + case HLIL_FOR: + [&]() { + const auto initExpr = instr.GetInitExpr<HLIL_FOR>(); + const auto condExpr = instr.GetConditionExpr<HLIL_FOR>(); + const auto updateExpr = instr.GetUpdateExpr<HLIL_FOR>(); + const auto loopExpr = instr.GetLoopExpr<HLIL_FOR>(); + + if (asFullAst) + { + tokens.Append(KeywordToken, "for "); + tokens.AppendOpenParen(); + if (initExpr.operation != HLIL_NOP) + GetExprTextInternal(initExpr, tokens, settings, asFullAst); + tokens.Append(TextToken, "; "); + if (condExpr.operation != HLIL_NOP) + GetExprTextInternal(condExpr, tokens, settings, asFullAst); + tokens.Append(TextToken, "; "); + if (updateExpr.operation != HLIL_NOP) + GetExprTextInternal(updateExpr, tokens, settings, asFullAst); + tokens.AppendCloseParen(); + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(loopExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + } + else + { + tokens.Append(KeywordToken, "while "); + tokens.AppendOpenParen(); + GetExprTextInternal(condExpr, tokens, settings); + tokens.AppendCloseParen(); + } + }(); + break; + + case HLIL_IF: + [&]() { + const auto condExpr = instr.GetConditionExpr<HLIL_IF>(); + const auto trueExpr = instr.GetTrueExpr<HLIL_IF>(); + const auto falseExpr = instr.GetFalseExpr<HLIL_IF>(); + + tokens.Append(KeywordToken, "if "); + tokens.AppendOpenParen(); + GetExprTextInternal(condExpr, tokens, settings, asFullAst); + tokens.AppendCloseParen(); + if (!asFullAst) + return; + + auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr); + tokens.BeginScope(scopeType); + + GetExprTextInternal(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + //tokens.SetCurrentExpr(falseExpr); + if (falseExpr.operation == HLIL_IF) + { + tokens.ScopeContinuation(false); + tokens.Append(KeywordToken, "else "); + GetExprTextInternal(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + } + else if (falseExpr.operation != HLIL_NOP) + { + tokens.ScopeContinuation(false); + tokens.Append(KeywordToken, "else"); + scopeType = HighLevelILFunction::GetExprScopeType(falseExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + } + else + { + tokens.FinalizeScope(); + } + }(); + break; + + case HLIL_WHILE: + [&]() { + const auto condExpr = instr.GetConditionExpr<HLIL_WHILE>(); + const auto loopExpr = instr.GetLoopExpr<HLIL_WHILE>(); + + tokens.Append(KeywordToken, "while "); + tokens.AppendOpenParen(); + GetExprTextInternal(condExpr, tokens, settings); + tokens.AppendCloseParen(); + if (!asFullAst) + return; + + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + + }(); + break; + + case HLIL_DO_WHILE: + [&]() { + const auto loopExpr = instr.GetLoopExpr<HLIL_DO_WHILE>(); + const auto condExpr = instr.GetConditionExpr<HLIL_DO_WHILE>(); + if (asFullAst) + { + tokens.Append(KeywordToken, "do"); + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprTextInternal(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, true); + tokens.EndScope(scopeType); + tokens.ScopeContinuation(true); + tokens.Append(KeywordToken, "while "); + tokens.AppendOpenParen(); + GetExprTextInternal(condExpr, tokens, settings); + tokens.AppendCloseParen(); + tokens.Append(KeywordToken, ";"); + tokens.FinalizeScope(); + } + else + { + tokens.Append(TextToken, "/* do */ "); + tokens.Append(KeywordToken, "while "); + tokens.AppendOpenParen(); + GetExprTextInternal(condExpr, tokens, settings); + tokens.AppendCloseParen(); + } + }(); + break; + + case HLIL_SWITCH: + [&]() { + const auto condExpr = instr.GetConditionExpr<HLIL_SWITCH>(); + const auto caseExprs = instr.GetCases<HLIL_SWITCH>(); + const auto defaultExpr = instr.GetDefaultExpr<HLIL_SWITCH>(); + + tokens.Append(KeywordToken, "switch "); + tokens.AppendOpenParen(); + GetExprTextInternal(condExpr, tokens, settings, asFullAst); + tokens.AppendCloseParen(); + tokens.BeginScope(SwitchScopeType); + if (!asFullAst) + return; + + for (const auto caseExpr : caseExprs) + { + GetExprTextInternal(caseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.NewLine(); + } + + if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE) + { + tokens.Append(KeywordToken, "default"); + tokens.Append(TextToken, ":"); + tokens.BeginScope(CaseScopeType); + GetExprTextInternal(defaultExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + tokens.EndScope(CaseScopeType); + tokens.FinalizeScope(); + } + + tokens.EndScope(SwitchScopeType); + tokens.FinalizeScope(); + + }(); + break; + + case HLIL_CASE: + [&]() { + const auto valueExprs = instr.GetValueExprs<HLIL_CASE>(); + const auto trueExpr = instr.GetTrueExpr<HLIL_CASE>(); + + for (size_t index{}; index < valueExprs.size(); index++) + { + const auto& valueExpr = valueExprs[index]; + tokens.Append(KeywordToken, "case "); + GetExprTextInternal(valueExpr, tokens, settings, asFullAst); + tokens.Append(TextToken, ":"); + if (index != valueExprs.size() - 1) + tokens.NewLine(); + } + + if (!asFullAst) + return; + + tokens.BeginScope(CaseScopeType); + GetExprTextInternal(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, true); + + static const std::vector<BNHighLevelILOperation> operations { + HLIL_CONTINUE, HLIL_NORET, HLIL_UNREACHABLE, HLIL_JUMP, HLIL_GOTO, HLIL_TAILCALL}; + + // If the case doesn't have an instruction that implicitly exits the case, append a break statement + // at the end of the case + if (trueExpr.operation == HLIL_BLOCK) + { + const auto& blockExprs = trueExpr.GetBlockExprs<HLIL_BLOCK>(); + bool disallowedOperation = false; + for (const auto expr : blockExprs) + { + if (std::find(operations.begin(), operations.end(), expr.operation) != operations.end()) + { + disallowedOperation = true; + break; + } + } + + if (!disallowedOperation) + { + tokens.NewLine(); + tokens.Append(KeywordToken, "break"); + tokens.Append(TextToken, ";"); + } + } + else if (std::find(operations.begin(), operations.end(), trueExpr.operation) == operations.end()) + { + tokens.NewLine(); + tokens.Append(KeywordToken, "break"); + tokens.Append(TextToken, ";"); + } + + tokens.EndScope(CaseScopeType); + tokens.FinalizeScope(); + }(); + break; + + case HLIL_BREAK: + [&]() { + tokens.Append(KeywordToken, "break"); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CONTINUE: + [&]() { + tokens.Append(KeywordToken, "continue"); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ZX: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_ZX>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + AppendSizeToken(instr.size, false, tokens); + tokens.AppendCloseParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, false, false); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_SX: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_SX>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + AppendSizeToken(instr.size, true, tokens); + tokens.AppendCloseParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, false, true); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CALL: + [&]() { + const auto destExpr = instr.GetDestExpr<HLIL_CALL>(); + const auto parameterExprs = instr.GetParameterExprs<HLIL_CALL>(); + + GetExprTextInternal(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence); + tokens.AppendOpenParen(); + + vector<FunctionParameter> namedParams; + Ref<Type> functionType = instr.GetDestExpr<HLIL_CALL>().GetType(); + if (functionType && (functionType->GetClass() == PointerTypeClass) + && (functionType->GetChildType()->GetClass() == FunctionTypeClass)) + namedParams = functionType->GetChildType()->GetParameters(); + + for (size_t index{}; index < parameterExprs.size(); index++) + { + const auto& parameterExpr = parameterExprs[index]; + if (index != 0) tokens.Append(TextToken, ", "); + + // If the type of the parameter is known to be a pointer to a string, then we directly render it as a + // string, regardless of its length + bool renderedAsString = false; + if (index < namedParams.size() && parameterExprs[index].operation == HLIL_CONST_PTR) + { + auto exprType = namedParams[index].type; + if (exprType && (exprType->GetClass() == PointerTypeClass)) + { + if (auto child = exprType->GetChildType(); child) + { + if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1) + || child->IsWideChar()) + { + tokens.AppendPointerTextToken(parameterExprs[index], + parameterExprs[index].GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, + precedence, true); + renderedAsString = true; + } + } + } + } + + if (!renderedAsString) + GetExprText(parameterExpr, tokens, settings, asFullAst); + } + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_IMPORT: + [&]() { + const auto constant = instr.GetConstant<HLIL_IMPORT>(); + auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant); + const auto symbolType = symbol->GetType(); + + if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol)) + { + symbol = Symbol::ImportedFunctionFromImportAddressSymbol(symbol, constant); + const auto symbolShortName = symbol->GetShortName(); + tokens.Append(IndirectImportToken, NoTokenContext, symbolShortName, instr.address, constant, instr.size, instr.sourceOperand); + return; + } + + tokens.AppendPointerTextToken(instr, constant, settings, DereferenceNonDataSymbols, precedence); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ARRAY_INDEX: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_ARRAY_INDEX>(); + const auto indexExpr = instr.GetIndexExpr<HLIL_ARRAY_INDEX>(); + + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence); + tokens.AppendOpenBracket(); + GetExprTextInternal(indexExpr, tokens, settings, asFullAst); + tokens.AppendCloseBracket(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_VAR_INIT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_VAR_INIT>(); + const auto destExpr = instr.GetDestVariable<HLIL_VAR_INIT>(); + + const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(destExpr); + const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform(); + const auto prevTypeTokens = + variableType ? + variableType->GetTokensBeforeName(platform, variableType.GetConfidence()) : + vector<InstructionTextToken>{}; + const auto postTypeTokens = + variableType ? + variableType->GetTokensAfterName(platform, variableType.GetConfidence()) : + vector<InstructionTextToken>{}; + + // Check to see if the variable appears live + bool appearsDead = false; + if (const auto ssaForm = instr.GetSSAForm(); ssaForm.operation == HLIL_VAR_INIT_SSA) + { + const auto ssaDest = ssaForm.GetDestSSAVariable<HLIL_VAR_INIT_SSA>(); + appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest); + } + + // If the variable does not appear live, show the assignment as zero confidence (grayed out) + if (appearsDead) + tokens.BeginForceZeroConfidence(); + + if (variableType) + { + for (auto typeToken: prevTypeTokens) + { + typeToken.context = LocalVariableTokenContext; + typeToken.address = destExpr.ToIdentifier(); + tokens.Append(typeToken); + } + tokens.Append(TextToken, " "); + } + tokens.AppendVarTextToken(destExpr, instr, instr.size); + if (variableType) + { + for (auto typeToken: postTypeTokens) + { + typeToken.context = LocalVariableTokenContext; + typeToken.address = destExpr.ToIdentifier(); + tokens.Append(typeToken); + } + } + tokens.Append(OperationToken, " = "); + + // For the right side of the assignment, only use zero confidence if the instruction does + // not have any side effects + if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr)) + { + tokens.EndForceZeroConfidence(); + appearsDead = false; + } + + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence); + + if (appearsDead) + tokens.EndForceZeroConfidence(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_VAR_DECLARE: + [&]() { + const auto variable = instr.GetVariable<HLIL_VAR_DECLARE>(); + + const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(variable); + const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform(); + const auto prevTypeTokens = + variableType ? + variableType->GetTokensBeforeName(platform, variableType.GetConfidence()) : + vector<InstructionTextToken>{}; + const auto postTypeTokens = + variableType ? + variableType->GetTokensAfterName(platform, variableType.GetConfidence()) : + vector<InstructionTextToken>{}; + + if (variableType) + { + for (auto typeToken: prevTypeTokens) + { + typeToken.context = LocalVariableTokenContext; + typeToken.address = variable.ToIdentifier(); + tokens.Append(typeToken); + } + tokens.Append(TextToken, " "); + } + tokens.AppendVarTextToken(variable, instr, instr.size); + if (variableType) + { + for (auto typeToken: postTypeTokens) + { + typeToken.context = LocalVariableTokenContext; + typeToken.address = variable.ToIdentifier(); + tokens.Append(typeToken); + } + } + + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FLOAT_CONST: + [&]() { + const auto constant = instr.GetConstant<HLIL_FLOAT_CONST>(); + if (instr.size == 4) + { + char valueStr[64]; + union + { + float f; + uint32_t i; + } bits{}; + bits.i = constant; + snprintf(valueStr, sizeof(valueStr), "%.9gf", bits.f); + tokens.Append(FloatingPointToken, InstructionAddressTokenContext, valueStr, instr.address); + } + else if (instr.size == 8) + { + char valueStr[64]; + union + { + double f; + uint64_t i; + } bits{}; + bits.i = constant; + snprintf(valueStr, sizeof(valueStr), "%.17g", bits.f); + string s = valueStr; + if ((s.find('.') == string::npos) && (s.find('e') == string::npos)) + s += ".0"; + tokens.Append(FloatingPointToken, InstructionAddressTokenContext, s, instr.address); + } + else + { + tokens.AppendIntegerTextToken(instr, constant, 8); + } + + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CONST: + [&]() { + tokens.AppendConstantTextToken(instr, instr.GetConstant<HLIL_CONST>(), instr.size, settings, precedence); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CONST_DATA: + [&]() { + // Constant data should be rendered according to the type of builtin function being used. + const ConstantData& data = instr.GetConstantData<HLIL_CONST_DATA>(); + if (auto [db, builtin] = data.ToDataBuffer(); db.GetLength()) + { + switch (builtin) + { + case BuiltinStrcpy: + case BuiltinStrncpy: + { + string result(db.ToEscapedString(true)); + tokens.Append(BraceToken, "\""); + tokens.Append(StringToken, ConstStringDataTokenContext, result, instr.address, data.value); + tokens.Append(BraceToken, "\""); + break; + } + case BuiltinMemset: + { + char buf[32]; + if (data.value < 10) + snprintf(buf, sizeof(buf), "%" PRId64 "", data.value); + else + snprintf(buf, sizeof(buf), "0x%" PRIx64 "", data.value); + + tokens.Append(BraceToken, "{"); + tokens.Append(StringToken, ConstDataTokenContext, string(buf), instr.address, data.value); + tokens.Append(BraceToken, "}"); + break; + } + default: + { + if (auto unicode = GetFunction()->GetView()->StringifyUnicodeData(instr.function->GetArchitecture(), db); unicode.has_value()) + { + auto wideStringPrefix = (builtin == BuiltinWcscpy) ? "L" : ""; + auto tokenContext = (builtin == BuiltinWcscpy) ? ConstStringDataTokenContext : ConstDataTokenContext; + tokens.Append(BraceToken, wideStringPrefix + string("\"")); + tokens.Append(StringToken, tokenContext, unicode.value().first, instr.address, data.value); + tokens.Append(BraceToken, "\""); + } + else + { + string result(db.ToEscapedString(false, true)); + + tokens.Append(BraceToken, "\""); + tokens.Append(StringToken, ConstDataTokenContext, result, instr.address, data.value); + tokens.Append(BraceToken, "\""); + // TODO controls for emitting an initializer list? + // char str[32]; + // string result; + // const uint8_t* bytes = (const uint8_t*)db.GetData(); + // for (size_t i = 0; i < db.GetLength(); i++) + // { + // snprintf(str, sizeof(str), "0x%" PRIx8 ", ", bytes[i]); + // result += str; + // } + // if (result.size() > 2) + // result.erase(result.end() - 2); + // tokens.Append(StringToken, StringDisplayTokenContext, string("{ ") + result + string(" }"), instr.address, bytes.value); + } + break; + } + } + } + else + tokens.Append(StringToken, StringDisplayTokenContext, string("<invalid constant data>"), instr.address, data.value); + + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CONST_PTR: + [&]() { + tokens.AppendPointerTextToken( + instr, instr.GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, precedence); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_VAR: + [&]() { + const auto variable = instr.GetVariable<HLIL_VAR>(); + const auto variableName = GetHighLevelILFunction()->GetFunction()->GetVariableNameOrDefault(variable); + tokens.Append(LocalVariableToken, LocalVariableTokenContext, variableName, + instr.exprIndex, variable.ToIdentifier(), instr.size); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ASSIGN: + [&]() { + const auto destExpr = instr.GetDestExpr<HLIL_ASSIGN>(); + const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN>(); + + // Check to see if the variable appears live + bool appearsDead = false; + if (destExpr.operation == HLIL_VAR_SSA) + { + const auto ssaForm = destExpr.GetSSAVariable<HLIL_VAR_SSA>(); + appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaForm); + } + else if (destExpr.operation == HLIL_VAR) + { + if (const auto ssaForm = destExpr.GetSSAForm(); ssaForm.operation == HLIL_VAR_SSA) + { + const auto ssaDest = ssaForm.GetSSAVariable<HLIL_VAR_SSA>(); + appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest); + } + } + + // If the variable does not appear live, show the assignment as zero confidence (grayed out) + if (appearsDead) + tokens.BeginForceZeroConfidence(); + + std::optional<string> assignUpdateOperator; + std::optional<HighLevelILInstruction> assignUpdateSource; + bool assignUpdateNegate = false; + const auto isSplit = destExpr.operation == HLIL_SPLIT; + std::optional<bool> assignSignedHint; + if (isSplit) + { + const auto high = destExpr.GetHighExpr<HLIL_SPLIT>(); + const auto low = destExpr.GetLowExpr<HLIL_SPLIT>(); + + GetExprTextInternal(high, tokens, settings, asFullAst, precedence); + tokens.Append(OperationToken, " = "); + tokens.Append(OperationToken, "HIGH"); + AppendSingleSizeToken(high.size, OperationToken, tokens); + tokens.AppendOpenParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence); + tokens.AppendCloseParen(); + tokens.AppendSemicolon(); + tokens.NewLine(); + GetExprTextInternal(low, tokens, settings, asFullAst, precedence); + } + else + { + // Check for assignment with an operator on the same variable as the destination + // (for example, `a = a + 2` should be shown as `a += 2`) + if ((srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_SUB || srcExpr.operation == HLIL_MUL + || srcExpr.operation == HLIL_DIVU || srcExpr.operation == HLIL_DIVS + || srcExpr.operation == HLIL_LSL || srcExpr.operation == HLIL_LSR + || srcExpr.operation == HLIL_ASR || (instr.size != 0 && srcExpr.operation == HLIL_AND) + || (instr.size != 0 && srcExpr.operation == HLIL_OR) + || (instr.size != 0 && srcExpr.operation == HLIL_XOR)) + && (srcExpr.GetLeftExpr() == destExpr)) + { + auto lessThanZero = [](uint64_t value, uint64_t width) -> bool { + return ((1UL << ((width * 8) - 1UL)) & value) != 0; + }; + switch (srcExpr.operation) + { + case HLIL_ADD: + assignUpdateOperator = " += "; + assignUpdateSource = srcExpr.GetRightExpr(); + + if ((assignUpdateSource.value().operation == HLIL_CONST) + && lessThanZero( + assignUpdateSource.value().GetConstant<HLIL_CONST>(), assignUpdateSource.value().size) + && assignUpdateSource.value().size >= instr.size) + { + // Convert addition of a negative constant into subtraction of a positive constant + assignUpdateOperator = " -= "; + assignUpdateNegate = true; + } + break; + case HLIL_SUB: + assignUpdateOperator = " -= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_MUL: + assignUpdateOperator = " *= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_DIVU: + assignUpdateOperator = " u/= "; + assignUpdateSource = srcExpr.GetRightExpr(); + assignSignedHint = false; + break; + case HLIL_DIVS: + assignUpdateOperator = " s/= "; + assignUpdateSource = srcExpr.GetRightExpr(); + assignSignedHint = true; + break; + case HLIL_LSL: + assignUpdateOperator = " <<= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_LSR: + assignUpdateOperator = " u>>= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_ASR: + assignUpdateOperator = " s>>= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_AND: + assignUpdateOperator = " &= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_OR: + assignUpdateOperator = " |= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_XOR: + assignUpdateOperator = " ^= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + default: + break; + } + } + else if ( + (srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_MUL + || (instr.size != 0 && srcExpr.operation == HLIL_AND) + || (instr.size != 0 && srcExpr.operation == HLIL_OR) + || (instr.size != 0 && srcExpr.operation == HLIL_XOR)) + && (srcExpr.GetRightExpr() == destExpr)) + { + switch (srcExpr.operation) + { + case HLIL_ADD: + assignUpdateOperator = " += "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + case HLIL_MUL: + assignUpdateOperator = " *= "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + case HLIL_AND: + assignUpdateOperator = " &= "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + case HLIL_OR: + assignUpdateOperator = " |= "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + case HLIL_XOR: + assignUpdateOperator = " ^= "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + default: + break; + } + } + } + + GetExprTextInternal(destExpr, tokens, settings, asFullAst, precedence); + if (assignUpdateOperator.has_value() && assignUpdateSource.has_value()) + tokens.Append(OperationToken, assignUpdateOperator.value()); + else + tokens.Append(OperationToken, " = "); + + // For the right side of the assignment, only use zero confidence if the instruction does + // not have any side effects + if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr)) + { + tokens.EndForceZeroConfidence(); + appearsDead = false; + } + + if (isSplit) + { +// const auto high = destExpr.GetHighExpr<HLIL_SPLIT>(); + const auto low = destExpr.GetLowExpr<HLIL_SPLIT>(); + + tokens.Append(OperationToken, "LOW"); + AppendSingleSizeToken(low.size, OperationToken, tokens); + tokens.AppendOpenParen(); + } + + if (assignUpdateOperator.has_value() && assignUpdateSource.has_value()) + { + if (assignUpdateNegate) + { + tokens.AppendIntegerTextToken(assignUpdateSource.value(), + -BNSignExtend( + assignUpdateSource.value().GetConstant<HLIL_CONST>(), assignUpdateSource.value().size, 8), + assignUpdateSource.value().size); + } + else + { + GetExprTextInternal(assignUpdateSource.value(), tokens, settings, asFullAst, + AssignmentOperatorPrecedence, false, assignSignedHint); + } + } + else + { + GetExprTextInternal( + srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence, false, assignSignedHint); + } + + if (isSplit) + tokens.AppendCloseParen(); + + if (appearsDead) + tokens.EndForceZeroConfidence(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ASSIGN_UNPACK: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN_UNPACK>(); + const auto destExprs = instr.GetDestExprs<HLIL_ASSIGN_UNPACK>(); + const auto firstExpr = destExprs[0]; + + GetExprTextInternal(firstExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence); + tokens.Append(OperationToken, " = "); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_STRUCT_FIELD: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_STRUCT_FIELD>(); + const auto fieldOffset = instr.GetOffset<HLIL_STRUCT_FIELD>(); + const auto memberIndex = instr.GetMemberIndex<HLIL_STRUCT_FIELD>(); + + const auto type = GetFieldType(srcExpr, false); + const auto fieldDisplayType = GetFieldDisplayType(type, fieldOffset, memberIndex, false); + if (fieldDisplayType == FieldDisplayOffset) + { + tokens.Append(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens); + tokens.Append(TextToken, "*"); + tokens.AppendCloseParen(); + } + tokens.AppendOpenParen(); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + tokens.Append(TypeNameToken, "char"); + tokens.Append(TextToken, "*"); + tokens.AppendCloseParen(); + } + GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + + tokens.Append(OperationToken, " + "); + tokens.AppendIntegerTextToken(instr, fieldOffset, instr.size); + tokens.AppendCloseParen(); + + char offsetStr[64]; + snprintf(offsetStr, sizeof(offsetStr), "0x%" PRIx64, fieldOffset); + + vector<string> nameList { offsetStr }; + HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( + GetFunction()->GetView(), type, srcExpr, nameList); + } + else if (fieldDisplayType == FieldDisplayMemberOffset) + { + tokens.Append(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens); + tokens.Append(TextToken, "*"); + tokens.AppendCloseParen(); + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + tokens.Append(TypeNameToken, "char"); + tokens.Append(TextToken, "*"); + tokens.AppendCloseParen(); + } + GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendCloseParen(); + } + /* rest is rendered in AppendFieldTextTokens */ + } + else + { + GetExprTextInternal(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + } + + AppendFieldTextTokens(srcExpr, fieldOffset, memberIndex, instr.size, tokens, false); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_DEREF: + [&]() { + auto srcExpr = instr.GetSourceExpr<HLIL_DEREF>(); + + auto appendMaybeBrace = [&](const InstructionTextToken& token) + { + if (token.type == BraceToken) + { + if (token.text == "(") + tokens.AppendOpenParen(); + else if (token.text == ")") + tokens.AppendCloseParen(); + else if (token.text == "[") + tokens.AppendOpenBracket(); + else if (token.text == "]") + tokens.AppendCloseBracket(); + else if (token.text == "{") + tokens.AppendOpenBrace(); + else if (token.text == "}") + tokens.AppendCloseBrace(); + else + tokens.Append(token); + } + else + { + tokens.Append(token); + } + }; + + if (srcExpr.operation == HLIL_CONST_PTR) + { + const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>(); + + vector<InstructionTextToken> pointerTokens{}; + if (AppendPointerTextToken(srcExpr, constant, pointerTokens, settings, DereferenceNonDataSymbols, precedence) == DataSymbolResult) + { + const auto type = srcExpr.GetType(); + if (type && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth()) + { + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + } + tokens.Append(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + AppendSizeToken(instr.size, false, tokens); + tokens.Append(TextToken, "*"); + tokens.AppendCloseParen(); + } + + for (const auto& token : pointerTokens) + { + appendMaybeBrace(token); + } + + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendCloseParen(); + } + } + else + { + for (const auto& token : pointerTokens) + { + appendMaybeBrace(token); + } + } + } + else + { + for (const auto& token : pointerTokens) + { + appendMaybeBrace(token); + } + } + } + else + { + size_t derefCount{}; + while (srcExpr.operation == HLIL_DEREF) + { + auto next = srcExpr.GetSourceExpr<HLIL_DEREF>(); + if (next.size == srcExpr.size) + { + srcExpr = srcExpr.GetSourceExpr<HLIL_DEREF>(); + derefCount++; + } + else + { + break; + } + } + + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + for (size_t index{}; index <= derefCount; index++) + tokens.Append(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + + AppendSizeToken(instr.size, false, tokens); + + for (size_t index{}; index <= derefCount; index++) + tokens.Append(TextToken, "*"); + tokens.AppendCloseParen(); + } + + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + } + + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_TAILCALL: + [&]() { + const auto destExpr = instr.GetDestExpr<HLIL_TAILCALL>(); + const auto parameterExprs = instr.GetParameterExprs<HLIL_TAILCALL>(); + + tokens.Append(AnnotationToken, "/* tailcall */"); + tokens.NewLine(); + tokens.Append(KeywordToken, "return "); + GetExprTextInternal(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence); + tokens.AppendOpenParen(); + for (size_t index{}; index < parameterExprs.size(); index++) + { + const auto& parameterExpr = parameterExprs[index]; + if (index != 0) tokens.Append(TextToken, ", "); + GetExprTextInternal(parameterExpr, tokens, settings, asFullAst); + } + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ADDRESS_OF: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_ADDRESS_OF>(); + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.Append(OperationToken, "&"); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_E: + [&]() { + bool parens = precedence > EqualityOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendComparison(" == ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CMP_E: + [&]() { + if ((instr.GetRightExpr<HLIL_CMP_E>().operation == HLIL_CONST + || instr.GetRightExpr<HLIL_CMP_E>().operation == HLIL_CONST_PTR) + && instr.GetRightExpr<HLIL_CMP_E>().GetConstant() == 0) + { + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.Append(OperationToken, "!"); + GetExprTextInternal(instr.GetLeftExpr<HLIL_CMP_E>(), tokens, settings, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + } + else + { + bool parens = precedence > EqualityOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendComparison(" == ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + } + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_NE: + [&]() { + bool parens = precedence > EqualityOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendComparison(" != ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CMP_NE: + [&]() { + if ((instr.GetRightExpr<HLIL_CMP_NE>().operation == HLIL_CONST + || instr.GetRightExpr<HLIL_CMP_NE>().operation == HLIL_CONST_PTR) + && instr.GetRightExpr<HLIL_CMP_NE>().GetConstant() == 0) + { + GetExprTextInternal(instr.GetLeftExpr<HLIL_CMP_NE>(), tokens, settings, precedence); + } + else + { + bool parens = precedence > EqualityOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendComparison(" != ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + } + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_LT: + case HLIL_CMP_SLT: + case HLIL_CMP_ULT: + [&]() { + bool parens = precedence > CompareOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> cmpSigned; + if (instr.operation == HLIL_CMP_ULT) + cmpSigned = false; + else if (instr.operation == HLIL_CMP_SLT) + cmpSigned = true; + AppendComparison(" < ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_LE: + case HLIL_CMP_SLE: + case HLIL_CMP_ULE: + [&]() { + bool parens = precedence > CompareOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> cmpSigned; + if (instr.operation == HLIL_CMP_ULE) + cmpSigned = false; + else if (instr.operation == HLIL_CMP_SLE) + cmpSigned = true; + AppendComparison(" <= ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_GE: + case HLIL_CMP_SGE: + case HLIL_CMP_UGE: + [&]() { + bool parens = precedence > CompareOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> cmpSigned; + if (instr.operation == HLIL_CMP_UGE) + cmpSigned = false; + else if (instr.operation == HLIL_CMP_SGE) + cmpSigned = true; + AppendComparison(" >= ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_GT: + case HLIL_CMP_SGT: + case HLIL_CMP_UGT: + [&]() { + bool parens = precedence > CompareOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> cmpSigned; + if (instr.operation == HLIL_CMP_UGT) + cmpSigned = false; + else if (instr.operation == HLIL_CMP_SGT) + cmpSigned = true; + AppendComparison(" > ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + + case HLIL_AND: + [&]() { + bool parens = instr.size == 0 ? + precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalOrOperatorPrecedence : + precedence >= EqualityOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(instr.size == 0 ? " && " : " & ", instr, tokens, settings, asFullAst, + instr.size == 0 ? LogicalAndOperatorPrecedence : BitwiseAndOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_OR: + [&]() { + bool parens = (instr.size == 0) ? + precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalAndOperatorPrecedence : + precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(instr.size == 0 ? " || " : " | ", instr, tokens, settings, asFullAst, + instr.size == 0 ? LogicalOrOperatorPrecedence : BitwiseOrOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_XOR: + [&]() { + bool parens = precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || + precedence == BitwiseOrOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" ^ ", instr, tokens, settings, asFullAst, BitwiseXorOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ADC: + case HLIL_ADD_OVERFLOW: + case HLIL_FADD: + case HLIL_ADD: + [&]() { + bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || + precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" + ", instr, tokens, settings, asFullAst, AddOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_SUB: + [&]{ + // Check for offset pointers + auto left = instr.GetLeftExpr<HLIL_SUB>(); + auto right = instr.GetRightExpr<HLIL_SUB>(); + if (left.operation == HLIL_VAR && right.operation == HLIL_CONST) + { + auto var = left.GetVariable<HLIL_VAR>(); + auto srcOffset = right.GetConstant<HLIL_CONST>(); + auto varType = GetFunction()->GetVariableType(var); + if (varType + && varType->GetClass() == PointerTypeClass + && varType->GetNamedTypeReference() + && varType->GetOffset() == srcOffset) + { + // Yes + tokens.Append(OperationToken, "ADJ"); + tokens.AppendOpenParen(); + GetExprTextInternal(left, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + tokens.AppendCloseParen(); + return; + } + } + + // No + bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || + precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" - ", instr, tokens, settings, asFullAst, SubOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + case HLIL_SBB: + case HLIL_FSUB: + [&]() { + bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || + precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" - ", instr, tokens, settings, asFullAst, SubOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_LSL: + [&]() { + bool parens = precedence > ShiftOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" << ", instr, tokens, settings, asFullAst, ShiftOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_LSR: + case HLIL_ASR: + [&]() { + bool parens = precedence > ShiftOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" >> ", instr, tokens, settings, asFullAst, ShiftOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + + case HLIL_FMUL: + case HLIL_MUL: + case HLIL_MULU_DP: + case HLIL_MULS_DP: + [&]() { + bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence || + precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> mulSigned; + if (instr.operation == HLIL_MULU_DP) + mulSigned = false; + else if (instr.operation == HLIL_MULS_DP) + mulSigned = true; + AppendTwoOperand(" * ", instr, tokens, settings, asFullAst, MultiplyOperatorPrecedence, mulSigned); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FDIV: + case HLIL_DIVU: + case HLIL_DIVU_DP: + case HLIL_DIVS: + case HLIL_DIVS_DP: + [&]() { + bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence + || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence + || precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> divSigned; + if (instr.operation == HLIL_DIVU || instr.operation == HLIL_DIVU_DP) + divSigned = false; + else if (instr.operation == HLIL_DIVS || instr.operation == HLIL_DIVS_DP) + divSigned = true; + AppendTwoOperand(" / ", instr, tokens, settings, asFullAst, DivideOperatorPrecedence, divSigned); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_MODU: + case HLIL_MODU_DP: + case HLIL_MODS: + case HLIL_MODS_DP: + [&]() { + bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence + || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence + || precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> modSigned; + if (instr.operation == HLIL_MODU || instr.operation == HLIL_MODU_DP) + modSigned = false; + else if (instr.operation == HLIL_MODS || instr.operation == HLIL_MODS_DP) + modSigned = true; + AppendTwoOperand(" % ", instr, tokens, settings, asFullAst, DivideOperatorPrecedence, modSigned); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + + case HLIL_ROR: + [&]() { + AppendTwoOperandFunction("ROR", instr, tokens, settings); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ROL: + [&]() { + AppendTwoOperandFunction("ROL", instr, tokens, settings); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_RLC: + [&]() { + AppendTwoOperandFunctionWithCarry("RLC", instr, tokens, settings); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_RRC: + [&]() { + AppendTwoOperandFunctionWithCarry("RRC", instr, tokens, settings); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_TEST_BIT: + [&]() { + AppendTwoOperandFunction("TEST_BIT", instr, tokens, settings); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FLOOR: + [&]() { + tokens.Append(OperationToken, "floor"); + tokens.AppendOpenParen(); + GetExprTextInternal(instr.GetSourceExpr<HLIL_FLOOR>(), tokens, settings); + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CEIL: + [&]() { + tokens.Append(OperationToken, "ceil"); + tokens.AppendOpenParen(); + GetExprTextInternal(instr.GetSourceExpr<HLIL_CEIL>(), tokens, settings); + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FTRUNC: + [&]() { + auto src = instr.GetSourceExpr<HLIL_FTRUNC>(); + string trunc; + if (src.size == 4) + trunc = "truncf"; + else if (src.size == 8) + trunc = "trunc"; + else if (src.size == 10) + trunc = "truncl"; + else + trunc = "trunc" + std::to_string(src.size) + "f"; + tokens.Append(OperationToken, trunc); + tokens.AppendOpenParen(); + GetExprTextInternal(src, tokens, settings); + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FABS: + [&]() { + auto src = instr.GetSourceExpr<HLIL_FABS>(); + string fabs; + if (src.size == 4) + fabs = "fabsf"; + else if (src.size == 8) + fabs = "fabs"; + else if (src.size == 10) + fabs = "fabsl"; + else + fabs = "fabs" + std::to_string(src.size) + "f"; + tokens.Append(OperationToken, fabs); + tokens.AppendOpenParen(); + GetExprTextInternal(src, tokens, settings); + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FSQRT: + [&]() { + auto src = instr.GetSourceExpr<HLIL_FSQRT>(); + string sqrt; + if (src.size == 4) + sqrt = "sqrtf"; + else if (src.size == 8) + sqrt = "sqrt"; + else if (src.size == 10) + sqrt = "sqrtl"; + else + sqrt = "sqrt" + std::to_string(src.size) + "f"; + tokens.Append(OperationToken, sqrt); + tokens.AppendOpenParen(); + GetExprTextInternal(src, tokens, settings); + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ROUND_TO_INT: + [&]() { + AppendTwoOperandFunction("round", instr, tokens, settings, false); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_O: + [&]() { + AppendTwoOperandFunction("FCMP_O", instr, tokens, settings, false); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_UO: + [&]() { + AppendTwoOperandFunction("FCMP_UO", instr, tokens, settings, false); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + + case HLIL_NOT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_NOT>(); + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.Append(OperationToken, instr.size == 0 ? "!" : "~"); + GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FNEG: + case HLIL_NEG: + [&]() { + const auto srcExpr = instr.GetSourceExpr(); + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.Append(OperationToken, "-"); + tokens.AppendOpenParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, false, true); + tokens.AppendCloseParen(); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FLOAT_CONV: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_CONV>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + const auto floatType = + instr.size == 2 ? "float16_t" : + instr.size == 4 ? "float" : + instr.size == 8 ? "double" : + instr.size == 10 ? "long double" : "float" + std::to_string(instr.size) + "_t"; + + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + tokens.Append(TypeNameToken, floatType.c_str()); + tokens.AppendCloseParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FLOAT_TO_INT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_TO_INT>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + AppendSizeToken(instr.size, true, tokens); + tokens.AppendCloseParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_BOOL_TO_INT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_BOOL_TO_INT>(); + + bool parens = precedence > TernaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, TernaryOperatorPrecedence); + tokens.Append(OperationToken, " ? "); + tokens.AppendIntegerTextToken(instr, 1, 1); + tokens.Append(OperationToken, " : "); + tokens.AppendIntegerTextToken(instr, 0, 1); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_INT_TO_FLOAT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_INT_TO_FLOAT>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + const auto floatType = + instr.size == 2 ? "float16_t" : + instr.size == 4 ? "float" : + instr.size == 8 ? "double" : + instr.size == 10 ? "long double" : "float" + std::to_string(instr.size) + "_t"; + + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + tokens.Append(TypeNameToken, floatType.c_str()); + tokens.AppendCloseParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_INTRINSIC: + [&]() { + const auto intrinsic = instr.GetIntrinsic<HLIL_INTRINSIC>(); + const auto intrinsicName = GetHighLevelILFunction()->GetArchitecture()->GetIntrinsicName(intrinsic); + const auto parameterExprs = instr.GetParameterExprs<HLIL_INTRINSIC>(); + + tokens.Append(KeywordToken, intrinsicName, intrinsic); + tokens.AppendOpenParen(); + for (size_t index{}; index < parameterExprs.size(); index++) + { + const auto& parameterExpr = parameterExprs[index]; + if (index != 0) tokens.Append(TextToken, ", "); + GetExprTextInternal(parameterExpr, tokens, settings, asFullAst); + } + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_RET: + [&]() { + const auto srcExprs = instr.GetSourceExprs<HLIL_RET>(); + + tokens.Append(KeywordToken, "return"); + for (size_t index{}; index < srcExprs.size(); index++) + { + const auto& srcExpr = srcExprs[index]; + if (index == 0) tokens.Append(TextToken, " "); + if (index != 0) tokens.Append(TextToken, ", "); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst); + } + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_NORET: + [&]() { + tokens.Append(AnnotationToken, "/* no return */"); + }(); + break; + + case HLIL_UNREACHABLE: + [&]() { + tokens.Append(AnnotationToken, "/* unreachable */"); + }(); + break; + + case HLIL_JUMP: + [&]() { + const auto destExpr = instr.GetDestExpr<HLIL_JUMP>(); + tokens.Append(AnnotationToken, "/* jump -> "); + GetExprTextInternal(destExpr, tokens, settings); + tokens.Append(AnnotationToken, " */"); + }(); + break; + + case HLIL_UNDEF: + [&]() { + tokens.Append(AnnotationToken, "/* undefined */"); + }(); + break; + + case HLIL_TRAP: + [&]() { + const auto vector = instr.GetVector<HLIL_TRAP>(); + tokens.Append(KeywordToken, "trap"); + tokens.AppendOpenParen(); + tokens.AppendIntegerTextToken(instr, vector, 8); + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_DEREF_FIELD: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_DEREF_FIELD>(); + const auto offset = instr.GetOffset<HLIL_DEREF_FIELD>(); + const auto memberIndex = instr.GetMemberIndex<HLIL_DEREF_FIELD>(); + auto type = srcExpr.GetType().GetValue(); + + if (type && (type->GetClass() == PointerTypeClass)) + type = type->GetChildType().GetValue(); + + if (type && (type->GetClass() == NamedTypeReferenceClass)) + type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference()); + + bool derefOffset = false; + if (type && (type->GetClass() == StructureTypeClass)) + { + std::optional<size_t> memberIndexHint; + if (memberIndex != BN_INVALID_EXPR) + memberIndexHint = memberIndex; + + bool outer = true; + if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0, + [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset, + uint64_t adjustedOffset, const StructureMember& member) { + BNSymbolDisplayResult symbolType; + if (srcExpr.operation == HLIL_CONST_PTR) + { + const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>(); + symbolType = tokens.AppendPointerTextToken( + srcExpr, constant, settings, DisplaySymbolOnly, precedence); + } + else + { + GetExprTextInternal( + srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + symbolType = OtherSymbolResult; + } + + const auto displayDeref = symbolType != DataSymbolResult; + if (displayDeref && outer) + tokens.Append(OperationToken, "->"); + else + tokens.Append(OperationToken, "."); + outer = false; + + vector<string> nameList {member.name}; + HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( + GetFunction()->GetView(), type, srcExpr, nameList); + + tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0, + BN_FULL_CONFIDENCE, nameList); + }), + memberIndexHint) + return; + } + else if (type && (type->GetClass() == StructureTypeClass)) + { + derefOffset = true; + } + + if (derefOffset || offset != 0) + { + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + + tokens.Append(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + AppendSizeToken(!derefOffset ? srcExpr.size : instr.size, true, tokens); + tokens.Append(TextToken, "*"); + tokens.AppendCloseParen(); + } + tokens.AppendOpenParen(); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + tokens.Append(TypeNameToken, "char"); + tokens.Append(TextToken, "*"); + tokens.AppendCloseParen(); + } + + if (srcExpr.operation == HLIL_CONST_PTR) + { + const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>(); + tokens.AppendPointerTextToken(srcExpr, constant, settings, DisplaySymbolOnly, precedence); + } + else + { + GetExprTextInternal(srcExpr, tokens, settings, true, AddOperatorPrecedence); + } + + tokens.Append(OperationToken, " + "); + tokens.AppendIntegerTextToken(instr, offset, instr.size); + tokens.AppendCloseParen(); + if (parens) + tokens.AppendCloseParen(); + } + + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_EXTERN_PTR: + [&]() { + const int64_t val = instr.GetOffset<HLIL_EXTERN_PTR>(); + if (val != 0) + tokens.AppendOpenParen(); + tokens.AppendPointerTextToken( + instr, instr.GetConstant<HLIL_EXTERN_PTR>(), settings, AddressOfDataSymbols, precedence); + if (val != 0) + { + char valStr[32]; + if (val >= 0) + { + tokens.Append(OperationToken, " + "); + if (val <= 9) + snprintf(valStr, sizeof(valStr), "%" PRIx64, val); + else + snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val); + } + else + { + tokens.Append(OperationToken, " - "); + if (val >= -9) + snprintf(valStr, sizeof(valStr), "%" PRIx64, -val); + else + snprintf(valStr, sizeof(valStr), "0x%" PRIx64, -val); + } + tokens.Append(IntegerToken, valStr, val); + tokens.AppendCloseParen(); + } + + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_SYSCALL: + [&]() { + tokens.Append(KeywordToken, "syscall"); + tokens.AppendOpenParen(); + const auto operandList = instr.GetParameterExprs<HLIL_SYSCALL>(); + vector<FunctionParameter> namedParams; + bool skipSyscallNumber = false; + if (GetFunction() && (operandList.size() > 0) && (operandList[0].operation == HLIL_CONST)) + { + const auto platform = GetFunction()->GetPlatform(); + if (platform) + { + const auto syscall = (uint32_t)operandList[0].GetConstant<HLIL_CONST>(); + const auto syscallName = platform->GetSystemCallName(syscall); + if (settings && settings->GetCallParameterHints() != NeverShowParameterHints) + { + const auto functionType = platform->GetSystemCallType(syscall); + if (functionType && (functionType->GetClass() == FunctionTypeClass)) + namedParams = functionType->GetParameters(); + } + if (syscallName.length()) + { + tokens.Append(TextToken, syscallName); + tokens.Append(TextToken, " "); + tokens.AppendOpenBrace(); + GetExprTextInternal(operandList[0], tokens, settings); + tokens.AppendCloseBrace(); + skipSyscallNumber = true; + } + } + } + for (size_t i = (skipSyscallNumber ? 1 : 0); i < operandList.size(); i++) + { + if (i != 0) + tokens.Append(TextToken, ", "); + GetExprTextInternal(operandList[i], tokens, settings); + } + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_BP: + [&]() { + tokens.Append(KeywordToken, "breakpoint"); + tokens.AppendOpenParen(); + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_UNIMPL_MEM: + case HLIL_UNIMPL: + [&]() { + const auto hlilFunc = GetHighLevelILFunction(); + const auto instructionText = hlilFunc->GetExprText(hlilFunc->GetInstruction( + hlilFunc->GetInstructionForExpr(instr.exprIndex)).exprIndex, true, settings); + tokens.Append(AnnotationToken, "/* "); + for (const auto& token : instructionText[0].tokens) + tokens.Append(token.type, token.text, token.value); + + if (instructionText.size() > 1) + tokens.Append(AnnotationToken, "..."); + + tokens.Append(AnnotationToken, " */"); + }(); + break; + + case HLIL_NOP: + [&]() { + tokens.Append(AnnotationToken, "/* nop */"); + }(); + break; + + case HLIL_GOTO: + [&]() { + const auto target = instr.GetTarget<HLIL_GOTO>(); + tokens.Append(KeywordToken, "goto "); + tokens.Append(GotoLabelToken, GetFunction()->GetGotoLabelName(target), target); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_LABEL: + [&]() { + const auto target = instr.GetTarget<HLIL_LABEL>(); + tokens.DecreaseIndent(); + tokens.Append(GotoLabelToken, GetFunction()->GetGotoLabelName(target), target); + tokens.Append(TextToken, ":"); + tokens.IncreaseIndent(); + }(); + break; + + case HLIL_LOW_PART: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_LOW_PART>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + AppendSizeToken(instr.size, signedHint.value_or(true), tokens); + tokens.AppendCloseParen(); + GetExprTextInternal(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (statement) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_SPLIT: break; + default: + [&]() { + char buf[64]{}; + snprintf(buf, sizeof(buf), "/* <UNIMPLEMENTED, %x> */", instr.operation); + tokens.Append(AnnotationToken, buf); + }(); + break; + } + + if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType()) + { + tokens.AppendCloseParen(); + } +} + + +string PseudoCFunction::GetAnnotationStartString() const +{ + // Show annotations as C-style inline comments + return "/* "; +} + + +string PseudoCFunction::GetAnnotationEndString() const +{ + // Show annotations as C-style inline comments + return " */"; +} + + +PseudoCFunctionType::PseudoCFunctionType(): LanguageRepresentationFunctionType("Pseudo C") +{ +} + + +Ref<LanguageRepresentationFunction> PseudoCFunctionType::Create(Architecture* arch, Function* owner, + HighLevelILFunction* highLevelILFunction) +{ + return new PseudoCFunction(arch, owner, highLevelILFunction); +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifndef DEMO_EDITION + BINARYNINJAPLUGIN void CorePluginDependencies() + { + } +#endif + +#ifdef DEMO_EDITION + bool PseudoCPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + LanguageRepresentationFunctionType* type = new PseudoCFunctionType(); + LanguageRepresentationFunctionType::Register(type); + return true; + } +} diff --git a/lang/c/pseudoc.h b/lang/c/pseudoc.h new file mode 100644 index 00000000..664e7695 --- /dev/null +++ b/lang/c/pseudoc.h @@ -0,0 +1,66 @@ +#pragma once + +#include "binaryninjaapi.h" + +class PseudoCFunction: public BinaryNinja::LanguageRepresentationFunction +{ + BinaryNinja::Ref<BinaryNinja::HighLevelILFunction> m_highLevelIL; + + enum FieldDisplayType + { + FieldDisplayName, + FieldDisplayOffset, + FieldDisplayMemberOffset, + FieldDisplayNone + }; + + BinaryNinja::Ref<BinaryNinja::Type> GetFieldType(const BinaryNinja::HighLevelILInstruction& var, bool deref); + FieldDisplayType GetFieldDisplayType(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t offset, size_t memberIndex, bool deref); + + BNSymbolDisplayResult AppendPointerTextToken(const BinaryNinja::HighLevelILInstruction& instr, int64_t val, + std::vector<BinaryNinja::InstructionTextToken>& tokens, BinaryNinja::DisassemblySettings* settings, + BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence); + std::string GetSizeToken(size_t size, bool isSigned); + void AppendSizeToken(size_t size, bool isSigned, BinaryNinja::HighLevelILTokenEmitter& emitter); + void AppendSingleSizeToken(size_t size, BNInstructionTextTokenType type, BinaryNinja::HighLevelILTokenEmitter& emitter); + void AppendComparison(const std::string& comparison, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst, + BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt); + void AppendTwoOperand(const std::string& operand, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst, + BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt); + void AppendTwoOperandFunction(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool sizeToken = true); + void AppendTwoOperandFunctionWithCarry(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings); + void AppendFieldTextTokens(const BinaryNinja::HighLevelILInstruction& var, uint64_t offset, size_t memberIndex, size_t size, + BinaryNinja::HighLevelILTokenEmitter& tokens, bool deref, bool displayDeref = true); + void GetExprTextInternal(const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool asFullAst = true, + BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false, + std::optional<bool> signedHint = std::nullopt); + +protected: + void InitTokenEmitter(BinaryNinja::HighLevelILTokenEmitter& tokens) override; + void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens, + BinaryNinja::DisassemblySettings* settings, bool asFullAst = true, + BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, bool statement = false) override; + void BeginLines( + const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override; + void EndLines( + const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override; + +public: + PseudoCFunction(BinaryNinja::Architecture* arch, BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction); + + std::string GetAnnotationStartString() const override; + std::string GetAnnotationEndString() const override; +}; + +class PseudoCFunctionType: public BinaryNinja::LanguageRepresentationFunctionType +{ +public: + PseudoCFunctionType(); + BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch, + BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override; +}; diff --git a/lang/rust/CMakeLists.txt b/lang/rust/CMakeLists.txt new file mode 100644 index 00000000..e374d8e9 --- /dev/null +++ b/lang/rust/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(lang_pseudorust) + +if(NOT BN_INTERNAL_BUILD) + add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api) +endif() + +file(GLOB SOURCES + *.cpp + *.h) + +if(DEMO) + add_library(lang_pseudorust STATIC ${SOURCES}) +else() + add_library(lang_pseudorust SHARED ${SOURCES}) +endif() + +target_include_directories(lang_pseudorust + PRIVATE ${PROJECT_SOURCE_DIR}) + +if(WIN32) + target_link_directories(lang_pseudorust + PRIVATE ${BN_INSTALL_DIR}) + target_link_libraries(lang_pseudorust binaryninjaapi binaryninjacore) +else() + target_link_libraries(lang_pseudorust binaryninjaapi) +endif() + +set_target_properties(lang_pseudorust PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + C_STANDARD 99 + C_STANDARD_REQUIRED ON + C_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON) + +if(BN_INTERNAL_BUILD) + plugin_rpath(lang_pseudorust) + set_target_properties(lang_pseudorust PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +endif() diff --git a/lang/rust/pseudorust.cpp b/lang/rust/pseudorust.cpp new file mode 100644 index 00000000..278bbaa8 --- /dev/null +++ b/lang/rust/pseudorust.cpp @@ -0,0 +1,2925 @@ +#include <inttypes.h> +#include "pseudorust.h" +#include "rusttypes.h" +#include "highlevelilinstruction.h" + +using namespace std; +using namespace BinaryNinja; + + +PseudoRustFunction::PseudoRustFunction( + Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) : + LanguageRepresentationFunction(arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction) +{ +} + + +void PseudoRustFunction::InitTokenEmitter(HighLevelILTokenEmitter& tokens) +{ + // Braces must always be turned on for Rust + tokens.SetBraceRequirement(BracesAlwaysRequired); + + // Multiple statements in a `match` require braces around them + tokens.SetBracesAroundSwitchCases(true); + + // If the user hasn't specified a preference on brace placement, use the Rust standard style + tokens.SetDefaultBracesOnSameLine(true); + + // Rust doesn't allow omitting the braces around conditional bodies + tokens.SetSimpleScopeAllowed(false); +} + + +void PseudoRustFunction::BeginLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens) +{ + if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex) + { + // At top level, add braces around the entire function + tokens.AppendOpenBrace(); + tokens.NewLine(); + tokens.IncreaseIndent(); + } +} + + +void PseudoRustFunction::EndLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens) +{ + if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex) + { + // At top level, add braces around the entire function + tokens.NewLine(); + tokens.DecreaseIndent(); + tokens.AppendCloseBrace(); + } +} + + +BNSymbolDisplayResult PseudoRustFunction::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val, + vector<InstructionTextToken>& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence) +{ + Confidence<Ref<Type>> type = instr.GetType(); + if (type && (type->GetClass() == PointerTypeClass) && type->IsConst()) + { + string stringValue; + size_t childWidth = 0; + if (auto child = type->GetChildType(); child) + childWidth = child->GetWidth(); + if (auto strType = GetFunction()->GetView()->CheckForStringAnnotationType(val, stringValue, false, false, childWidth); strType.has_value()) + { + if (symbolDisplay == DereferenceNonDataSymbols) + { + if (precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, "("); + tokens.emplace_back(OperationToken, "*"); + } + tokens.emplace_back(BraceToken, DisassemblyTextRenderer::GetStringLiteralPrefix(strType.value()) + string("\"")); + tokens.emplace_back(StringToken, StringReferenceTokenContext, stringValue, instr.address, strType.value()); + tokens.emplace_back(BraceToken, "\""); + if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, ")"); + return OtherSymbolResult; + } + } + + if (GetFunction()) + { + // If the pointer has a value of 0, check if it points to a valid address by + // 1. If the binary is relocatable, assign the pointer as nullptr + // 2. else, check if the constant zero which being referenced is a pointer(display as symbol) or not(display as nullptr) + if(val == 0x0 && type && (type->GetClass() == PointerTypeClass)) + { + if (GetFunction()->GetView()->IsRelocatable()) + { + if (symbolDisplay == DereferenceNonDataSymbols) + { + if (precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, "("); + tokens.emplace_back(OperationToken, "*"); + } + tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val); + if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, ")"); + return OtherSymbolResult; + } + + auto arch = GetHighLevelILFunction()->GetArchitecture(); + auto refs = GetHighLevelILFunction()->GetFunction()->GetConstantsReferencedByInstructionIfAvailable( + arch, instr.address); + bool constantZeroBeingReferencedIsPointer = false; + + for (const BNConstantReference& ref : refs) + if (ref.value == 0x0 && ref.pointer) + constantZeroBeingReferencedIsPointer = true; + if (!constantZeroBeingReferencedIsPointer) + { + if (symbolDisplay == DereferenceNonDataSymbols) + { + if (precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, "("); + tokens.emplace_back(OperationToken, "*"); + } + tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val); + if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, ")"); + return OtherSymbolResult; + } + } + + Ref<BinaryView> data = GetFunction()->GetView(); + vector<InstructionTextToken> symTokens; + BNSymbolDisplayResult result = DisassemblyTextRenderer::AddSymbolTokenStatic(symTokens, val, 0, + BN_INVALID_OPERAND, data, settings ? settings->GetMaximumSymbolWidth() : 0, GetFunction(), + BN_FULL_CONFIDENCE, symbolDisplay, precedence, instr.address); + if (result != NoSymbolAvailable) + { + for (auto& i : symTokens) + tokens.emplace_back(i); + return result; + } + } + + if (symbolDisplay == DereferenceNonDataSymbols) + { + if (precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, "("); + + tokens.emplace_back(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.emplace_back(BraceToken, "("); + tokens.emplace_back(TypeNameToken, GetSizeToken(instr.size, false)); + tokens.emplace_back(OperationToken, "*"); + tokens.emplace_back(BraceToken, ")"); + } + } + + char valStr[32]; + if (val >= 0) + { + if (val <= 9) + snprintf(valStr, sizeof(valStr), "%" PRIx64, val); + else + snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val); + } + else + { + if (val >= -9) + snprintf(valStr, sizeof(valStr), "-%" PRIx64, val); + else + snprintf(valStr, sizeof(valStr), "-0x%" PRIx64, val); + } + + tokens.emplace_back(PossibleAddressToken, InstructionAddressTokenContext, valStr, instr.address, val); + + if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) + tokens.emplace_back(BraceToken, ")"); + return OtherSymbolResult; +} + + +string PseudoRustFunction::GetSizeToken(size_t size, bool isSigned) +{ + char sizeStr[32]; + + switch (size) + { + case 0: + return {}; + case 1: + return (isSigned ? "i8" : "u8"); + case 2: + return (isSigned ? "i16" : "u16"); + case 4: + return (isSigned ? "i32" : "u32"); + case 8: + return (isSigned ? "i64" : "u64"); + case 10: + return (isSigned ? "i80" : "u80"); + case 16: + return (isSigned ? "i128" : "u128"); + } + + snprintf(sizeStr, sizeof(sizeStr), "%s%" PRIuPTR, isSigned ? "i" : "u", size); + return {sizeStr}; +} + + +void PseudoRustFunction::AppendSizeToken(size_t size, bool isSigned, HighLevelILTokenEmitter& emitter) +{ + const auto token = GetSizeToken(size, isSigned); + if (!token.empty()) + emitter.Append(TypeNameToken, token); +} + + +void PseudoRustFunction::AppendSingleSizeToken( + size_t size, BNInstructionTextTokenType type, HighLevelILTokenEmitter& emitter) +{ + char sizeStr[32]; + + switch (size) + { + case 0: + break; + case 1: + emitter.Append(type, "B"); + break; + case 2: + emitter.Append(type, "W"); + break; + case 4: + emitter.Append(type, "D"); + break; + case 8: + emitter.Append(type, "Q"); + break; + case 10: + emitter.Append(type, "T"); + break; + case 16: + emitter.Append(type, "O"); + break; + default: + snprintf(sizeStr, sizeof(sizeStr), "%" PRIuPTR "", size); + emitter.Append(type, sizeStr); + break; + } +} + + +void PseudoRustFunction::AppendComparison(const string& comparison, const HighLevelILInstruction& instr, + HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, + std::optional<bool> signedHint) +{ + const auto leftExpr = instr.GetLeftExpr(); + const auto rightExpr = instr.GetRightExpr(); + + GetExprText(leftExpr, emitter, settings, asFullAst, precedence, InnerExpression, signedHint); + emitter.Append(OperationToken, comparison); + GetExprText(rightExpr, emitter, settings, asFullAst, precedence, InnerExpression, signedHint); +} + + +void PseudoRustFunction::AppendTwoOperand(const string& operand, const HighLevelILInstruction& instr, + HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, + std::optional<bool> signedHint) +{ + const auto& twoOperand = instr.AsTwoOperand(); + const auto leftExpr = twoOperand.GetLeftExpr(); + const auto rightExpr = twoOperand.GetRightExpr(); + BNOperatorPrecedence leftPrecedence = precedence; + switch (precedence) + { + case SubOperatorPrecedence: + // Treat left side of subtraction as same level as addition. This lets + // (a - b) - c be represented as a - b - c, but a - (b - c) does not + // simplify at rendering + leftPrecedence = AddOperatorPrecedence; + break; + case DivideOperatorPrecedence: + // Treat left side of divison as same level as multiplication. This lets + // (a / b) / c be represented as a / b / c, but a / (b / c) does not + // simplify at rendering + leftPrecedence = MultiplyOperatorPrecedence; + break; + default: + break; + } + + if (leftExpr.operation == HLIL_SPLIT) + { + const auto low = leftExpr.GetLowExpr(); + const auto high = leftExpr.GetHighExpr(); + + emitter.Append(OperationToken, "COMBINE"); + emitter.AppendOpenParen(); + GetExprText(high, emitter, settings, asFullAst); + emitter.Append(TextToken, ", "); + GetExprText(low, emitter, settings, asFullAst); + emitter.AppendCloseParen(); + } + + if (operand == " + " || operand == " - ") + { + const auto exprType = leftExpr.GetType(); + if (exprType && exprType->IsPointer()) + { + GetExprText(leftExpr, emitter, settings, asFullAst, MemberAndFunctionOperatorPrecedence); + emitter.Append(TextToken, "."); + emitter.Append(OperationToken, "byte_offset"); + emitter.AppendOpenParen(); + if (operand == " - ") + { + emitter.Append(OperationToken, "-"); + GetExprText(rightExpr, emitter, settings, asFullAst, UnaryOperatorPrecedence); + } + else + { + GetExprText(rightExpr, emitter, settings, asFullAst); + } + emitter.AppendCloseParen(); + return; + } + } + + GetExprText(leftExpr, emitter, settings, asFullAst, leftPrecedence, InnerExpression, signedHint); + + auto lessThanZero = [](uint64_t value, uint64_t width) -> bool { + return ((1UL << ((width * 8) - 1UL)) & value) != 0; + }; + + if ((operand == " + ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size) && + rightExpr.size >= leftExpr.size) + { + // Convert addition of a negative constant into subtraction of a positive constant + emitter.Append(OperationToken, " - "); + emitter.AppendIntegerTextToken( + rightExpr, -BNSignExtend(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size); + return; + } + if ((operand == " - ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size) && + rightExpr.size >= leftExpr.size) + { + // Convert subtraction of a negative constant into addition of a positive constant + emitter.Append(OperationToken, " + "); + emitter.AppendIntegerTextToken( + rightExpr, -BNSignExtend(rightExpr.GetConstant<HLIL_CONST>(), rightExpr.size, 8), rightExpr.size); + return; + } + + emitter.Append(OperationToken, operand); + GetExprText(rightExpr, emitter, settings, asFullAst, precedence, InnerExpression, signedHint); +} + + +void PseudoRustFunction::AppendTwoOperandFunction(const string& function, + const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, + bool sizeToken) +{ + const auto& twoOperand = instr.AsTwoOperand(); + const auto leftExpr = twoOperand.GetLeftExpr(); + const auto rightExpr = twoOperand.GetRightExpr(); + + emitter.Append(OperationToken, function); + if (sizeToken) + AppendSingleSizeToken(twoOperand.size, OperationToken, emitter); + emitter.AppendOpenParen(); + + if (leftExpr.operation == HLIL_SPLIT) + { + const auto low = leftExpr.GetLowExpr(); + const auto high = leftExpr.GetHighExpr(); + + emitter.Append(OperationToken, "COMBINE"); + emitter.AppendOpenParen(); + GetExprText(high, emitter, settings); + emitter.Append(TextToken, ", "); + GetExprText(low, emitter, settings); + emitter.AppendCloseParen(); + } + + GetExprText(leftExpr, emitter, settings); + emitter.Append(TextToken, ", "); + GetExprText(rightExpr, emitter, settings); + + emitter.AppendCloseParen(); +} + + +void PseudoRustFunction::AppendTwoOperandMethodCall(const string& function, + const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings) +{ + const auto& twoOperand = instr.AsTwoOperand(); + const auto leftExpr = twoOperand.GetLeftExpr(); + const auto rightExpr = twoOperand.GetRightExpr(); + + if (leftExpr.operation == HLIL_SPLIT) + { + const auto low = leftExpr.GetLowExpr(); + const auto high = leftExpr.GetHighExpr(); + + emitter.Append(OperationToken, "COMBINE"); + emitter.AppendOpenParen(); + GetExprText(high, emitter, settings); + emitter.Append(TextToken, ", "); + GetExprText(low, emitter, settings); + emitter.AppendCloseParen(); + } + else + { + GetExprText(leftExpr, emitter, settings, MemberAndFunctionOperatorPrecedence); + } + + emitter.Append(TextToken, "."); + emitter.Append(OperationToken, function); + emitter.AppendOpenParen(); + GetExprText(rightExpr, emitter, settings); + emitter.AppendCloseParen(); +} + + +void PseudoRustFunction::AppendTwoOperandFunctionWithCarry(const string& function, + const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings) +{ + const auto leftExpr = instr.GetLeftExpr(); + const auto rightExpr = instr.GetRightExpr(); + const auto carryExpr = instr.GetCarryExpr(); + + tokens.Append(OperationToken, function); + AppendSingleSizeToken(instr.size, OperationToken, tokens); + tokens.AppendOpenParen(); + + if (leftExpr.operation == HLIL_SPLIT) + { + const auto low = leftExpr.GetLowExpr(); + const auto high = leftExpr.GetHighExpr(); + + tokens.Append(OperationToken, "COMBINE"); + tokens.AppendOpenParen(); + GetExprText(high, tokens, settings); + tokens.Append(TextToken, ", "); + GetExprText(low, tokens, settings); + tokens.AppendCloseParen(); + } + + GetExprText(leftExpr, tokens, settings); + tokens.Append(TextToken, ", "); + GetExprText(rightExpr, tokens, settings); + tokens.Append(TextToken, ", "); + GetExprText(carryExpr, tokens, settings); + + tokens.AppendCloseParen(); +} + + +Ref<Type> PseudoRustFunction::GetFieldType(const HighLevelILInstruction& var, bool deref) +{ + Ref<Type> type = var.GetType().GetValue(); + if (deref && type && (type->GetClass() == PointerTypeClass)) + type = type->GetChildType().GetValue(); + + if (type && (type->GetClass() == NamedTypeReferenceClass)) + type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference()); + + return type; +} + + +PseudoRustFunction::FieldDisplayType PseudoRustFunction::GetFieldDisplayType( + Ref<Type> type, uint64_t offset, size_t memberIndex, bool deref) +{ + if (type && (type->GetClass() == StructureTypeClass)) + { + std::optional<size_t> memberIndexHint; + if (memberIndex != BN_INVALID_EXPR) + memberIndexHint = memberIndex; + + if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0, + [&](NamedTypeReference*, Structure*, size_t, uint64_t, uint64_t, const StructureMember&) {}), + memberIndexHint) + return FieldDisplayName; + return FieldDisplayOffset; + } + else if (deref || offset != 0) + return FieldDisplayMemberOffset; + else + return FieldDisplayNone; +} + + +void PseudoRustFunction::AppendFieldTextTokens(const HighLevelILInstruction& var, uint64_t offset, + size_t memberIndex, size_t size, HighLevelILTokenEmitter& tokens, bool deref) +{ + const auto type = GetFieldType(var, deref); + const auto fieldDisplayType = GetFieldDisplayType(type, offset, memberIndex, deref); + switch (fieldDisplayType) + { + case FieldDisplayName: + { + std::optional<size_t> memberIndexHint; + if (memberIndex != BN_INVALID_EXPR) + memberIndexHint = memberIndex; + + if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0, + [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset, + uint64_t adjustedOffset, const StructureMember& member) { + tokens.Append(OperationToken, "."); + + vector<string> nameList {member.name}; + HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( + GetFunction()->GetView(), type, var, nameList); + + tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0, + BN_FULL_CONFIDENCE, nameList); + }), + memberIndexHint) + return; + + // Part of structure but no defined field, use __offset syntax + tokens.Append(OperationToken, "."); + char offsetStr[64]; + snprintf( + offsetStr, sizeof(offsetStr), "__offset(0x%" PRIx64 ")%s", offset, Type::GetSizeSuffix(size).c_str()); + + vector<string> nameList {offsetStr}; + HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(GetFunction()->GetView(), type, var, nameList); + + tokens.Append(StructOffsetToken, offsetStr, offset, size, 0, BN_FULL_CONFIDENCE, nameList); + return; + } + + case FieldDisplayOffset: + { + /* this is handled before the display */ + return; + } + + case FieldDisplayMemberOffset: + { + tokens.AppendOpenBracket(); + tokens.AppendIntegerTextToken(var, offset, size); + tokens.AppendCloseBracket(); + return; + } + + default: break; + } +} + + +bool PseudoRustFunction::IsMutable(const Variable& var) const +{ + for (auto i : GetHighLevelILFunction()->GetVariableDefinitions(var)) + { + auto expr = GetHighLevelILFunction()->GetExpr(i); + if (expr.operation == HLIL_VAR_DECLARE || expr.operation == HLIL_VAR_INIT) + continue; + return true; + } + return GetHighLevelILFunction()->GetAliasedVariables().count(var) != 0; +} + + +void PseudoRustFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, + DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, ExpressionType exprType, + std::optional<bool> signedHint) +{ + // The lambdas in this function are here to reduce stack frame size of this function. Without them, + // complex expression can cause the process to crash from a stack overflow. + auto exprGuard = tokens.SetCurrentExpr(instr); + + if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType()) + { + tokens.AppendOpenParen(); + tokens.AppendOpenParen(); + for (auto& token: instr.GetType()->GetTokens(GetArchitecture()->GetStandalonePlatform())) + { + tokens.Append(token); + } + tokens.AppendCloseParen(); + tokens.Append(TextToken, " "); + } + if (settings && settings->IsOptionSet(ShowILOpcodes)) + { + tokens.Append(OperationToken, "/*"); + switch (instr.operation) + { + case HLIL_NOP: tokens.Append(OperationToken, "HLIL_NOP"); break; + case HLIL_BLOCK: tokens.Append(OperationToken, "HLIL_BLOCK"); break; + case HLIL_IF: tokens.Append(OperationToken, "HLIL_IF"); break; + case HLIL_WHILE: tokens.Append(OperationToken, "HLIL_WHILE"); break; + case HLIL_DO_WHILE: tokens.Append(OperationToken, "HLIL_DO_WHILE"); break; + case HLIL_FOR: tokens.Append(OperationToken, "HLIL_FOR"); break; + case HLIL_SWITCH: tokens.Append(OperationToken, "HLIL_SWITCH"); break; + case HLIL_CASE: tokens.Append(OperationToken, "HLIL_CASE"); break; + case HLIL_BREAK: tokens.Append(OperationToken, "HLIL_BREAK"); break; + case HLIL_CONTINUE: tokens.Append(OperationToken, "HLIL_CONTINUE"); break; + case HLIL_JUMP: tokens.Append(OperationToken, "HLIL_JUMP"); break; + case HLIL_RET: tokens.Append(OperationToken, "HLIL_RET"); break; + case HLIL_NORET: tokens.Append(OperationToken, "HLIL_NORET"); break; + case HLIL_GOTO: tokens.Append(OperationToken, "HLIL_GOTO"); break; + case HLIL_LABEL: tokens.Append(OperationToken, "HLIL_LABEL"); break; + case HLIL_VAR_DECLARE: tokens.Append(OperationToken, "HLIL_VAR_DECLARE"); break; + case HLIL_VAR_INIT: tokens.Append(OperationToken, "HLIL_VAR_INIT"); break; + case HLIL_ASSIGN: tokens.Append(OperationToken, "HLIL_ASSIGN"); break; + case HLIL_ASSIGN_UNPACK: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK"); break; + case HLIL_VAR: tokens.Append(OperationToken, "HLIL_VAR"); break; + case HLIL_STRUCT_FIELD: tokens.Append(OperationToken, "HLIL_STRUCT_FIELD"); break; + case HLIL_ARRAY_INDEX: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX"); break; + case HLIL_SPLIT: tokens.Append(OperationToken, "HLIL_SPLIT"); break; + case HLIL_DEREF: tokens.Append(OperationToken, "HLIL_DEREF"); break; + case HLIL_DEREF_FIELD: tokens.Append(OperationToken, "HLIL_DEREF_FIELD"); break; + case HLIL_ADDRESS_OF: tokens.Append(OperationToken, "HLIL_ADDRESS_OF"); break; + case HLIL_CONST: tokens.Append(OperationToken, "HLIL_CONST"); break; + case HLIL_CONST_DATA: tokens.Append(OperationToken, "HLIL_CONST_DATA"); break; + case HLIL_CONST_PTR: tokens.Append(OperationToken, "HLIL_CONST_PTR"); break; + case HLIL_EXTERN_PTR: tokens.Append(OperationToken, "HLIL_EXTERN_PTR"); break; + case HLIL_FLOAT_CONST: tokens.Append(OperationToken, "HLIL_FLOAT_CONST"); break; + case HLIL_IMPORT: tokens.Append(OperationToken, "HLIL_IMPORT"); break; + case HLIL_ADD: tokens.Append(OperationToken, "HLIL_ADD"); break; + case HLIL_ADC: tokens.Append(OperationToken, "HLIL_ADC"); break; + case HLIL_SUB: tokens.Append(OperationToken, "HLIL_SUB"); break; + case HLIL_SBB: tokens.Append(OperationToken, "HLIL_SBB"); break; + case HLIL_AND: tokens.Append(OperationToken, "HLIL_AND"); break; + case HLIL_OR: tokens.Append(OperationToken, "HLIL_OR"); break; + case HLIL_XOR: tokens.Append(OperationToken, "HLIL_XOR"); break; + case HLIL_LSL: tokens.Append(OperationToken, "HLIL_LSL"); break; + case HLIL_LSR: tokens.Append(OperationToken, "HLIL_LSR"); break; + case HLIL_ASR: tokens.Append(OperationToken, "HLIL_ASR"); break; + case HLIL_ROL: tokens.Append(OperationToken, "HLIL_ROL"); break; + case HLIL_RLC: tokens.Append(OperationToken, "HLIL_RLC"); break; + case HLIL_ROR: tokens.Append(OperationToken, "HLIL_ROR"); break; + case HLIL_RRC: tokens.Append(OperationToken, "HLIL_RRC"); break; + case HLIL_MUL: tokens.Append(OperationToken, "HLIL_MUL"); break; + case HLIL_MULU_DP: tokens.Append(OperationToken, "HLIL_MULU_DP"); break; + case HLIL_MULS_DP: tokens.Append(OperationToken, "HLIL_MULS_DP"); break; + case HLIL_DIVU: tokens.Append(OperationToken, "HLIL_DIVU"); break; + case HLIL_DIVU_DP: tokens.Append(OperationToken, "HLIL_DIVU_DP"); break; + case HLIL_DIVS: tokens.Append(OperationToken, "HLIL_DIVS"); break; + case HLIL_DIVS_DP: tokens.Append(OperationToken, "HLIL_DIVS_DP"); break; + case HLIL_MODU: tokens.Append(OperationToken, "HLIL_MODU"); break; + case HLIL_MODU_DP: tokens.Append(OperationToken, "HLIL_MODU_DP"); break; + case HLIL_MODS: tokens.Append(OperationToken, "HLIL_MODS"); break; + case HLIL_MODS_DP: tokens.Append(OperationToken, "HLIL_MODS_DP"); break; + case HLIL_NEG: tokens.Append(OperationToken, "HLIL_NEG"); break; + case HLIL_NOT: tokens.Append(OperationToken, "HLIL_NOT"); break; + case HLIL_SX: tokens.Append(OperationToken, "HLIL_SX"); break; + case HLIL_ZX: tokens.Append(OperationToken, "HLIL_ZX"); break; + case HLIL_LOW_PART: tokens.Append(OperationToken, "HLIL_LOW_PART"); break; + case HLIL_CALL: tokens.Append(OperationToken, "HLIL_CALL"); break; + case HLIL_CMP_E: tokens.Append(OperationToken, "HLIL_CMP_E"); break; + case HLIL_CMP_NE: tokens.Append(OperationToken, "HLIL_CMP_NE"); break; + case HLIL_CMP_SLT: tokens.Append(OperationToken, "HLIL_CMP_SLT"); break; + case HLIL_CMP_ULT: tokens.Append(OperationToken, "HLIL_CMP_ULT"); break; + case HLIL_CMP_SLE: tokens.Append(OperationToken, "HLIL_CMP_SLE"); break; + case HLIL_CMP_ULE: tokens.Append(OperationToken, "HLIL_CMP_ULE"); break; + case HLIL_CMP_SGE: tokens.Append(OperationToken, "HLIL_CMP_SGE"); break; + case HLIL_CMP_UGE: tokens.Append(OperationToken, "HLIL_CMP_UGE"); break; + case HLIL_CMP_SGT: tokens.Append(OperationToken, "HLIL_CMP_SGT"); break; + case HLIL_CMP_UGT: tokens.Append(OperationToken, "HLIL_CMP_UGT"); break; + case HLIL_TEST_BIT: tokens.Append(OperationToken, "HLIL_TEST_BIT"); break; + case HLIL_BOOL_TO_INT: tokens.Append(OperationToken, "HLIL_BOOL_TO_INT"); break; + case HLIL_ADD_OVERFLOW: tokens.Append(OperationToken, "HLIL_ADD_OVERFLOW"); break; + case HLIL_SYSCALL: tokens.Append(OperationToken, "HLIL_SYSCALL"); break; + case HLIL_TAILCALL: tokens.Append(OperationToken, "HLIL_TAILCALL"); break; + case HLIL_INTRINSIC: tokens.Append(OperationToken, "HLIL_INTRINSIC"); break; + case HLIL_BP: tokens.Append(OperationToken, "HLIL_BP"); break; + case HLIL_TRAP: tokens.Append(OperationToken, "HLIL_TRAP"); break; + case HLIL_UNDEF: tokens.Append(OperationToken, "HLIL_UNDEF"); break; + case HLIL_UNIMPL: tokens.Append(OperationToken, "HLIL_UNIMPL"); break; + case HLIL_UNIMPL_MEM: tokens.Append(OperationToken, "HLIL_UNIMPL_MEM"); break; + case HLIL_FADD: tokens.Append(OperationToken, "HLIL_FADD"); break; + case HLIL_FSUB: tokens.Append(OperationToken, "HLIL_FSUB"); break; + case HLIL_FMUL: tokens.Append(OperationToken, "HLIL_FMUL"); break; + case HLIL_FDIV: tokens.Append(OperationToken, "HLIL_FDIV"); break; + case HLIL_FSQRT: tokens.Append(OperationToken, "HLIL_FSQRT"); break; + case HLIL_FNEG: tokens.Append(OperationToken, "HLIL_FNEG"); break; + case HLIL_FABS: tokens.Append(OperationToken, "HLIL_FABS"); break; + case HLIL_FLOAT_TO_INT: tokens.Append(OperationToken, "HLIL_FLOAT_TO_INT"); break; + case HLIL_INT_TO_FLOAT: tokens.Append(OperationToken, "HLIL_INT_TO_FLOAT"); break; + case HLIL_FLOAT_CONV: tokens.Append(OperationToken, "HLIL_FLOAT_CONV"); break; + case HLIL_ROUND_TO_INT: tokens.Append(OperationToken, "HLIL_ROUND_TO_INT"); break; + case HLIL_FLOOR: tokens.Append(OperationToken, "HLIL_FLOOR"); break; + case HLIL_CEIL: tokens.Append(OperationToken, "HLIL_CEIL"); break; + case HLIL_FTRUNC: tokens.Append(OperationToken, "HLIL_FTRUNC"); break; + case HLIL_FCMP_E: tokens.Append(OperationToken, "HLIL_FCMP_E"); break; + case HLIL_FCMP_NE: tokens.Append(OperationToken, "HLIL_FCMP_NE"); break; + case HLIL_FCMP_LT: tokens.Append(OperationToken, "HLIL_FCMP_LT"); break; + case HLIL_FCMP_LE: tokens.Append(OperationToken, "HLIL_FCMP_LE"); break; + case HLIL_FCMP_GE: tokens.Append(OperationToken, "HLIL_FCMP_GE"); break; + case HLIL_FCMP_GT: tokens.Append(OperationToken, "HLIL_FCMP_GT"); break; + case HLIL_FCMP_O: tokens.Append(OperationToken, "HLIL_FCMP_O"); break; + case HLIL_FCMP_UO: tokens.Append(OperationToken, "HLIL_FCMP_UO"); break; + case HLIL_UNREACHABLE: tokens.Append(OperationToken, "HLIL_UNREACHABLE"); break; + case HLIL_WHILE_SSA: tokens.Append(OperationToken, "HLIL_WHILE_SSA"); break; + case HLIL_DO_WHILE_SSA: tokens.Append(OperationToken, "HLIL_DO_WHILE_SSA"); break; + case HLIL_FOR_SSA: tokens.Append(OperationToken, "HLIL_FOR_SSA"); break; + case HLIL_VAR_INIT_SSA: tokens.Append(OperationToken, "HLIL_VAR_INIT_SSA"); break; + case HLIL_ASSIGN_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_MEM_SSA"); break; + case HLIL_ASSIGN_UNPACK_MEM_SSA: tokens.Append(OperationToken, "HLIL_ASSIGN_UNPACK_MEM_SSA"); break; + case HLIL_VAR_SSA: tokens.Append(OperationToken, "HLIL_VAR_SSA"); break; + case HLIL_ARRAY_INDEX_SSA: tokens.Append(OperationToken, "HLIL_ARRAY_INDEX_SSA"); break; + case HLIL_DEREF_SSA: tokens.Append(OperationToken, "HLIL_DEREF_SSA"); break; + case HLIL_DEREF_FIELD_SSA: tokens.Append(OperationToken, "HLIL_DEREF_FIELD_SSA"); break; + case HLIL_CALL_SSA: tokens.Append(OperationToken, "HLIL_CALL_SSA"); break; + case HLIL_SYSCALL_SSA: tokens.Append(OperationToken, "HLIL_SYSCALL_SSA"); break; + case HLIL_INTRINSIC_SSA: tokens.Append(OperationToken, "HLIL_INTRINSIC_SSA"); break; + case HLIL_VAR_PHI: tokens.Append(OperationToken, "HLIL_VAR_PHI"); break; + case HLIL_MEM_PHI: tokens.Append(OperationToken, "HLIL_MEM_PHI"); break; + } + tokens.Append(OperationToken, "*/"); + tokens.Append(TextToken, " "); + } + + switch (instr.operation) + { + case HLIL_BLOCK: + [&]() { + const auto exprs = instr.GetBlockExprs<HLIL_BLOCK>(); + bool needSeparator = false; + for (auto i = exprs.begin(); i != exprs.end(); ++i) + { + // Don't show void returns at the very end of the function when printing + // the root of an AST, as it is implicit and almost always omitted in + // normal source code. + auto next = i; + ++next; + if (asFullAst && (instr.exprIndex == GetHighLevelILFunction()->GetRootExpr().exprIndex) && (exprs.size() > 1) && + (next == exprs.end()) && ((*i).operation == HLIL_RET) && + ((*i).GetSourceExprs<HLIL_RET>().size() == 0)) + continue; + + // If the statement is one that contains additional blocks of code, insert a scope separator + // to visually separate the logic. + bool hasBlocks = false; + switch ((*i).operation) + { + case HLIL_IF: + case HLIL_WHILE: + case HLIL_WHILE_SSA: + case HLIL_DO_WHILE: + case HLIL_DO_WHILE_SSA: + case HLIL_FOR: + case HLIL_FOR_SSA: + case HLIL_SWITCH: + hasBlocks = true; + break; + default: + hasBlocks = false; + break; + } + if (needSeparator || (i != exprs.begin() && hasBlocks)) + { + tokens.ScopeSeparator(); + } + needSeparator = hasBlocks; + + // Emit the lines for the statement itself + GetExprText(*i, tokens, settings, true, TopLevelOperatorPrecedence, + exprType == TrailingStatementExpression && next == exprs.end() ? + TrailingStatementExpression : StatementExpression); + tokens.NewLine(); + } + }(); + break; + + case HLIL_FOR: + [&]() { + const auto initExpr = instr.GetInitExpr<HLIL_FOR>(); + const auto condExpr = instr.GetConditionExpr<HLIL_FOR>(); + const auto updateExpr = instr.GetUpdateExpr<HLIL_FOR>(); + const auto loopExpr = instr.GetLoopExpr<HLIL_FOR>(); + + if (asFullAst) + { + tokens.Append(KeywordToken, "for "); + + // If the loop can be represented as a ranged for in idiomatic Rust, show it that way + if (initExpr.operation == HLIL_VAR_INIT && + (condExpr.operation == HLIL_CMP_SLT || condExpr.operation == HLIL_CMP_SLE || + condExpr.operation == HLIL_CMP_ULT || condExpr.operation == HLIL_CMP_ULE) && + condExpr.GetLeftExpr().operation == HLIL_VAR && + condExpr.GetLeftExpr().GetVariable<HLIL_VAR>() == initExpr.GetDestVariable<HLIL_VAR_INIT>() && + updateExpr.operation == HLIL_ASSIGN && + updateExpr.GetDestExpr<HLIL_ASSIGN>() == condExpr.GetLeftExpr() && + updateExpr.GetSourceExpr<HLIL_ASSIGN>().operation == HLIL_ADD && + updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetLeftExpr<HLIL_ADD>() == condExpr.GetLeftExpr()) + { + bool stepBy = updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>().operation != HLIL_CONST || + updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>().GetConstant() != 1; + + const auto variable = initExpr.GetDestVariable<HLIL_VAR_INIT>(); + const auto variableName = GetHighLevelILFunction()->GetFunction()->GetVariableNameOrDefault(variable); + tokens.Append(LocalVariableToken, LocalVariableTokenContext, variableName, + instr.exprIndex, variable.ToIdentifier(), instr.size); + tokens.Append(KeywordToken, " in "); + + if (stepBy) + tokens.AppendOpenParen(); + + GetExprText(initExpr.GetSourceExpr<HLIL_VAR_INIT>(), tokens, settings, asFullAst, AssignmentOperatorPrecedence); + if (condExpr.operation == HLIL_CMP_SLT || condExpr.operation == HLIL_CMP_ULT) + tokens.Append(TextToken, ".."); + else + tokens.Append(TextToken, "..="); + GetExprText(condExpr.GetRightExpr(), tokens, settings, asFullAst, AssignmentOperatorPrecedence); + + if (stepBy) + { + tokens.AppendCloseParen(); + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "step_by"); + tokens.AppendOpenParen(); + GetExprText(updateExpr.GetSourceExpr<HLIL_ASSIGN>().GetRightExpr<HLIL_ADD>(), + tokens, settings, asFullAst); + tokens.AppendCloseParen(); + } + } + else + { + // For loop isn't directly representable in standard Rust + if (initExpr.operation != HLIL_NOP) + GetExprText(initExpr, tokens, settings, asFullAst); + tokens.Append(TextToken, "; "); + if (condExpr.operation != HLIL_NOP) + GetExprText(condExpr, tokens, settings, asFullAst); + tokens.Append(TextToken, "; "); + if (updateExpr.operation != HLIL_NOP) + GetExprText(updateExpr, tokens, settings, asFullAst); + } + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprText(loopExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, StatementExpression); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + } + else + { + tokens.Append(KeywordToken, "while "); + GetExprText(condExpr, tokens, settings); + } + }(); + break; + + case HLIL_IF: + [&]() { + const auto condExpr = instr.GetConditionExpr<HLIL_IF>(); + const auto trueExpr = instr.GetTrueExpr<HLIL_IF>(); + const auto falseExpr = instr.GetFalseExpr<HLIL_IF>(); + + tokens.Append(KeywordToken, "if "); + GetExprText(condExpr, tokens, settings, asFullAst); + if (!asFullAst) + return; + + auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr); + tokens.BeginScope(scopeType); + + GetExprText(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType); + tokens.EndScope(scopeType); + //tokens.SetCurrentExpr(falseExpr); + if (falseExpr.operation == HLIL_IF) + { + tokens.ScopeContinuation(false); + tokens.Append(KeywordToken, "else "); + GetExprText(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType); + } + else if (falseExpr.operation != HLIL_NOP) + { + tokens.ScopeContinuation(false); + tokens.Append(KeywordToken, "else"); + scopeType = HighLevelILFunction::GetExprScopeType(falseExpr); + tokens.BeginScope(scopeType); + GetExprText(falseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + } + else + { + tokens.FinalizeScope(); + } + }(); + break; + + case HLIL_WHILE: + [&]() { + const auto condExpr = instr.GetConditionExpr<HLIL_WHILE>(); + const auto loopExpr = instr.GetLoopExpr<HLIL_WHILE>(); + + if (condExpr.operation == HLIL_CONST && condExpr.GetConstant<HLIL_CONST>() != 0) + { + tokens.Append(KeywordToken, "loop"); + } + else + { + tokens.Append(KeywordToken, "while "); + GetExprText(condExpr, tokens, settings); + } + if (!asFullAst) + return; + + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprText(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, StatementExpression); + tokens.EndScope(scopeType); + tokens.FinalizeScope(); + + }(); + break; + + case HLIL_DO_WHILE: + [&]() { + const auto loopExpr = instr.GetLoopExpr<HLIL_DO_WHILE>(); + const auto condExpr = instr.GetConditionExpr<HLIL_DO_WHILE>(); + if (asFullAst) + { + tokens.Append(KeywordToken, "do"); + auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); + tokens.BeginScope(scopeType); + GetExprText(loopExpr, tokens, settings, true, TopLevelOperatorPrecedence, StatementExpression); + tokens.EndScope(scopeType); + tokens.ScopeContinuation(true); + tokens.Append(KeywordToken, "while "); + GetExprText(condExpr, tokens, settings); + tokens.Append(KeywordToken, ";"); + tokens.FinalizeScope(); + } + else + { + tokens.Append(TextToken, "/* do */ "); + tokens.Append(KeywordToken, "while "); + GetExprText(condExpr, tokens, settings); + } + }(); + break; + + case HLIL_SWITCH: + [&]() { + const auto condExpr = instr.GetConditionExpr<HLIL_SWITCH>(); + const auto caseExprs = instr.GetCases<HLIL_SWITCH>(); + const auto defaultExpr = instr.GetDefaultExpr<HLIL_SWITCH>(); + + tokens.Append(KeywordToken, "match "); + GetExprText(condExpr, tokens, settings, asFullAst); + tokens.BeginScope(SwitchScopeType); + if (!asFullAst) + return; + + for (const auto caseExpr : caseExprs) + { + GetExprText(caseExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType); + tokens.NewLine(); + } + + // Check for default case + if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE) + { + tokens.Append(TextToken, "_ =>"); + tokens.BeginScope(CaseScopeType); + GetExprText(defaultExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType); + tokens.EndScope(CaseScopeType); + tokens.FinalizeScope(); + } + + tokens.EndScope(SwitchScopeType); + tokens.FinalizeScope(); + + }(); + break; + + case HLIL_CASE: + [&]() { + const auto valueExprs = instr.GetValueExprs<HLIL_CASE>(); + const auto trueExpr = instr.GetTrueExpr<HLIL_CASE>(); + + for (size_t index{}; index < valueExprs.size(); index++) + { + const auto& valueExpr = valueExprs[index]; + if (index != 0) + tokens.Append(TextToken, " | "); + GetExprText(valueExpr, tokens, settings, asFullAst); + } + tokens.Append(TextToken, " =>"); + + if (!asFullAst) + return; + + tokens.BeginScope(CaseScopeType); + GetExprText(trueExpr, tokens, settings, asFullAst, TopLevelOperatorPrecedence, exprType); + tokens.EndScope(CaseScopeType); + tokens.FinalizeScope(); + }(); + break; + + case HLIL_BREAK: + [&]() { + tokens.Append(KeywordToken, "break"); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CONTINUE: + [&]() { + tokens.Append(KeywordToken, "continue"); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ZX: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_ZX>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprText(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + bool parens = precedence > LowUnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence, InnerExpression, false); + tokens.Append(KeywordToken, " as "); + AppendSizeToken(instr.size, false, tokens); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_SX: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_SX>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprText(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + bool parens = precedence > LowUnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence, InnerExpression, true); + tokens.Append(KeywordToken, " as "); + AppendSizeToken(instr.size, true, tokens); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CALL: + [&]() { + const auto destExpr = instr.GetDestExpr<HLIL_CALL>(); + const auto parameterExprs = instr.GetParameterExprs<HLIL_CALL>(); + + GetExprText(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence); + tokens.AppendOpenParen(); + + vector<FunctionParameter> namedParams; + Ref<Type> functionType = instr.GetDestExpr<HLIL_CALL>().GetType(); + if (functionType && (functionType->GetClass() == PointerTypeClass) + && (functionType->GetChildType()->GetClass() == FunctionTypeClass)) + namedParams = functionType->GetChildType()->GetParameters(); + + for (size_t index{}; index < parameterExprs.size(); index++) + { + const auto& parameterExpr = parameterExprs[index]; + if (index != 0) tokens.Append(TextToken, ", "); + + // If the type of the parameter is known to be a pointer to a string, then we directly render it as a + // string, regardless of its length + bool renderedAsString = false; + if (index < namedParams.size() && parameterExprs[index].operation == HLIL_CONST_PTR) + { + auto exprType = namedParams[index].type; + if (exprType && (exprType->GetClass() == PointerTypeClass)) + { + if (auto child = exprType->GetChildType(); child) + { + if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1) + || child->IsWideChar()) + { + tokens.AppendPointerTextToken(parameterExprs[index], + parameterExprs[index].GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, + precedence, true); + renderedAsString = true; + } + } + } + } + + if (!renderedAsString) + GetExprText(parameterExpr, tokens, settings, asFullAst); + } + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_IMPORT: + [&]() { + const auto constant = instr.GetConstant<HLIL_IMPORT>(); + auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant); + const auto symbolType = symbol->GetType(); + + if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol)) + { + symbol = Symbol::ImportedFunctionFromImportAddressSymbol(symbol, constant); + const auto symbolShortName = symbol->GetShortName(); + tokens.Append(IndirectImportToken, NoTokenContext, symbolShortName, instr.address, constant, instr.size, instr.sourceOperand); + return; + } + + tokens.AppendPointerTextToken(instr, constant, settings, DereferenceNonDataSymbols, precedence); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ARRAY_INDEX: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_ARRAY_INDEX>(); + const auto indexExpr = instr.GetIndexExpr<HLIL_ARRAY_INDEX>(); + + GetExprText(srcExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence); + tokens.AppendOpenBracket(); + GetExprText(indexExpr, tokens, settings, asFullAst); + tokens.AppendCloseBracket(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_VAR_INIT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_VAR_INIT>(); + const auto destExpr = instr.GetDestVariable<HLIL_VAR_INIT>(); + + const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(destExpr); + const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform(); + RustTypePrinter printer; + const auto prevTypeTokens = variableType ? + printer.GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) : + vector<InstructionTextToken> {}; + const auto postTypeTokens = variableType ? + printer.GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) : + vector<InstructionTextToken> {}; + + // Check to see if the variable appears live + bool appearsDead = false; + if (const auto ssaForm = instr.GetSSAForm(); ssaForm.operation == HLIL_VAR_INIT_SSA) + { + const auto ssaDest = ssaForm.GetDestSSAVariable<HLIL_VAR_INIT_SSA>(); + appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest); + } + + // If the variable does not appear live, show the assignment as zero confidence (grayed out) + if (appearsDead) + tokens.BeginForceZeroConfidence(); + + tokens.Append(KeywordToken, "let "); + + // Only show `mut` keyword if the variable is actually changed + if (IsMutable(destExpr)) + tokens.Append(KeywordToken, "mut "); + + if (variableType) + { + for (auto typeToken : prevTypeTokens) + { + typeToken.context = LocalVariableTokenContext; + typeToken.address = destExpr.ToIdentifier(); + tokens.Append(typeToken); + } + } + tokens.AppendVarTextToken(destExpr, instr, instr.size); + if (variableType) + { + for (auto typeToken : postTypeTokens) + { + typeToken.context = LocalVariableTokenContext; + typeToken.address = destExpr.ToIdentifier(); + tokens.Append(typeToken); + } + } + tokens.Append(OperationToken, " = "); + + // For the right side of the assignment, only use zero confidence if the instruction does + // not have any side effects + if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr)) + { + tokens.EndForceZeroConfidence(); + appearsDead = false; + } + + GetExprText(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence); + + if (appearsDead) + tokens.EndForceZeroConfidence(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_VAR_DECLARE: + [&]() { + const auto variable = instr.GetVariable<HLIL_VAR_DECLARE>(); + + const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(variable); + const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform(); + RustTypePrinter printer; + const auto prevTypeTokens = + variableType ? + printer.GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) : + vector<InstructionTextToken>{}; + const auto postTypeTokens = + variableType ? + printer.GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) : + vector<InstructionTextToken>{}; + + tokens.Append(KeywordToken, "let "); + + // Only show `mut` keyword if the variable is actually changed + if (IsMutable(variable)) + tokens.Append(KeywordToken, "mut "); + + if (variableType) + { + for (auto typeToken: prevTypeTokens) + { + typeToken.context = LocalVariableTokenContext; + typeToken.address = variable.ToIdentifier(); + tokens.Append(typeToken); + } + } + tokens.AppendVarTextToken(variable, instr, instr.size); + if (variableType) + { + for (auto typeToken: postTypeTokens) + { + typeToken.context = LocalVariableTokenContext; + typeToken.address = variable.ToIdentifier(); + tokens.Append(typeToken); + } + } + + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FLOAT_CONST: + [&]() { + const auto constant = instr.GetConstant<HLIL_FLOAT_CONST>(); + if (instr.size == 4) + { + char valueStr[64]; + union + { + float f; + uint32_t i; + } bits{}; + bits.i = constant; + snprintf(valueStr, sizeof(valueStr), "%.9gf", bits.f); + tokens.Append(FloatingPointToken, InstructionAddressTokenContext, valueStr, instr.address); + } + else if (instr.size == 8) + { + char valueStr[64]; + union + { + double f; + uint64_t i; + } bits{}; + bits.i = constant; + snprintf(valueStr, sizeof(valueStr), "%.17g", bits.f); + string s = valueStr; + if ((s.find('.') == string::npos) && (s.find('e') == string::npos)) + s += ".0"; + tokens.Append(FloatingPointToken, InstructionAddressTokenContext, s, instr.address); + } + else + { + tokens.AppendIntegerTextToken(instr, constant, 8); + } + + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CONST: + [&]() { + tokens.AppendConstantTextToken(instr, instr.GetConstant<HLIL_CONST>(), instr.size, settings, precedence); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CONST_DATA: + [&]() { + // Constant data should be rendered according to the type of builtin function being used. + const ConstantData& data = instr.GetConstantData<HLIL_CONST_DATA>(); + if (auto [db, builtin] = data.ToDataBuffer(); db.GetLength()) + { + switch (builtin) + { + case BuiltinStrcpy: + case BuiltinStrncpy: + { + string result(db.ToEscapedString(true)); + tokens.Append(BraceToken, "\""); + tokens.Append(StringToken, ConstStringDataTokenContext, result, instr.address, data.value); + tokens.Append(BraceToken, "\""); + break; + } + case BuiltinMemset: + { + char buf[32]; + if (data.value < 10) + snprintf(buf, sizeof(buf), "%" PRId64 "", data.value); + else + snprintf(buf, sizeof(buf), "0x%" PRIx64 "", data.value); + + tokens.Append(BraceToken, "{"); + tokens.Append(StringToken, ConstDataTokenContext, string(buf), instr.address, data.value); + tokens.Append(BraceToken, "}"); + break; + } + default: + { + if (auto unicode = GetFunction()->GetView()->StringifyUnicodeData(instr.function->GetArchitecture(), db); unicode.has_value()) + { + auto wideStringPrefix = (builtin == BuiltinWcscpy) ? "L" : ""; + auto tokenContext = (builtin == BuiltinWcscpy) ? ConstStringDataTokenContext : ConstDataTokenContext; + tokens.Append(BraceToken, wideStringPrefix + string("\"")); + tokens.Append(StringToken, tokenContext, unicode.value().first, instr.address, data.value); + tokens.Append(BraceToken, "\""); + } + else + { + string result(db.ToEscapedString(false, true)); + + tokens.Append(BraceToken, "\""); + tokens.Append(StringToken, ConstDataTokenContext, result, instr.address, data.value); + tokens.Append(BraceToken, "\""); + // TODO controls for emitting an initializer list? + // char str[32]; + // string result; + // const uint8_t* bytes = (const uint8_t*)db.GetData(); + // for (size_t i = 0; i < db.GetLength(); i++) + // { + // snprintf(str, sizeof(str), "0x%" PRIx8 ", ", bytes[i]); + // result += str; + // } + // if (result.size() > 2) + // result.erase(result.end() - 2); + // tokens.Append(StringToken, StringDisplayTokenContext, string("{ ") + result + string(" }"), instr.address, bytes.value); + } + break; + } + } + } + else + tokens.Append(StringToken, StringDisplayTokenContext, string("<invalid constant data>"), instr.address, data.value); + + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CONST_PTR: + [&]() { + tokens.AppendPointerTextToken( + instr, instr.GetConstant<HLIL_CONST_PTR>(), settings, AddressOfDataSymbols, precedence); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_VAR: + [&]() { + const auto variable = instr.GetVariable<HLIL_VAR>(); + const auto variableName = GetHighLevelILFunction()->GetFunction()->GetVariableNameOrDefault(variable); + tokens.Append(LocalVariableToken, LocalVariableTokenContext, variableName, + instr.exprIndex, variable.ToIdentifier(), instr.size); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ASSIGN: + [&]() { + const auto destExpr = instr.GetDestExpr<HLIL_ASSIGN>(); + const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN>(); + + // Check to see if the variable appears live + bool appearsDead = false; + if (destExpr.operation == HLIL_VAR_SSA) + { + const auto ssaForm = destExpr.GetSSAVariable<HLIL_VAR_SSA>(); + appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaForm); + } + else if (destExpr.operation == HLIL_VAR) + { + if (const auto ssaForm = destExpr.GetSSAForm(); ssaForm.operation == HLIL_VAR_SSA) + { + const auto ssaDest = ssaForm.GetSSAVariable<HLIL_VAR_SSA>(); + appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest); + } + } + + // If the variable does not appear live, show the assignment as zero confidence (grayed out) + if (appearsDead) + tokens.BeginForceZeroConfidence(); + + std::optional<string> assignUpdateOperator; + std::optional<HighLevelILInstruction> assignUpdateSource; + bool assignUpdateNegate = false; + const auto isSplit = destExpr.operation == HLIL_SPLIT; + std::optional<bool> assignSignHint; + if (isSplit) + { + const auto high = destExpr.GetHighExpr<HLIL_SPLIT>(); + const auto low = destExpr.GetLowExpr<HLIL_SPLIT>(); + + GetExprText(high, tokens, settings, asFullAst, precedence); + tokens.Append(OperationToken, " = "); + tokens.Append(OperationToken, "HIGH"); + AppendSingleSizeToken(high.size, OperationToken, tokens); + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, precedence); + tokens.AppendCloseParen(); + tokens.AppendSemicolon(); + tokens.NewLine(); + GetExprText(low, tokens, settings, asFullAst, precedence); + } + else + { + // Check for assignment with an operator on the same variable as the destination + // (for example, `a = a + 2` should be shown as `a += 2`) + if ((srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_SUB || srcExpr.operation == HLIL_MUL + || srcExpr.operation == HLIL_DIVU || srcExpr.operation == HLIL_DIVS + || srcExpr.operation == HLIL_LSL || srcExpr.operation == HLIL_LSR + || srcExpr.operation == HLIL_ASR || (instr.size != 0 && srcExpr.operation == HLIL_AND) + || (instr.size != 0 && srcExpr.operation == HLIL_OR) + || (instr.size != 0 && srcExpr.operation == HLIL_XOR)) + && (srcExpr.GetLeftExpr() == destExpr)) + { + auto lessThanZero = [](uint64_t value, uint64_t width) -> bool { + return ((1UL << ((width * 8) - 1UL)) & value) != 0; + }; + switch (srcExpr.operation) + { + case HLIL_ADD: + assignUpdateOperator = " += "; + assignUpdateSource = srcExpr.GetRightExpr(); + + if ((assignUpdateSource.value().operation == HLIL_CONST) + && lessThanZero( + assignUpdateSource.value().GetConstant<HLIL_CONST>(), assignUpdateSource.value().size) + && assignUpdateSource.value().size >= instr.size) + { + // Convert addition of a negative constant into subtraction of a positive constant + assignUpdateOperator = " -= "; + assignUpdateNegate = true; + } + break; + case HLIL_SUB: + assignUpdateOperator = " -= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_MUL: + assignUpdateOperator = " *= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_DIVU: + assignUpdateOperator = " /= "; + assignUpdateSource = srcExpr.GetRightExpr(); + assignSignHint = false; + break; + case HLIL_DIVS: + assignUpdateOperator = " /= "; + assignUpdateSource = srcExpr.GetRightExpr(); + assignSignHint = false; + break; + case HLIL_LSL: + assignUpdateOperator = " <<= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_LSR: + assignUpdateOperator = " >>= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_ASR: + assignUpdateOperator = " >>= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_AND: + assignUpdateOperator = " &= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_OR: + assignUpdateOperator = " |= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + case HLIL_XOR: + assignUpdateOperator = " ^= "; + assignUpdateSource = srcExpr.GetRightExpr(); + break; + default: + break; + } + } + else if ( + (srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_MUL + || (instr.size != 0 && srcExpr.operation == HLIL_AND) + || (instr.size != 0 && srcExpr.operation == HLIL_OR) + || (instr.size != 0 && srcExpr.operation == HLIL_XOR)) + && (srcExpr.GetRightExpr() == destExpr)) + { + switch (srcExpr.operation) + { + case HLIL_ADD: + assignUpdateOperator = " += "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + case HLIL_MUL: + assignUpdateOperator = " *= "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + case HLIL_AND: + assignUpdateOperator = " &= "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + case HLIL_OR: + assignUpdateOperator = " |= "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + case HLIL_XOR: + assignUpdateOperator = " ^= "; + assignUpdateSource = srcExpr.GetLeftExpr(); + break; + default: + break; + } + } + } + + GetExprText(destExpr, tokens, settings, asFullAst, precedence); + if (assignUpdateOperator.has_value() && assignUpdateSource.has_value()) + tokens.Append(OperationToken, assignUpdateOperator.value()); + else + tokens.Append(OperationToken, " = "); + + // For the right side of the assignment, only use zero confidence if the instruction does + // not have any side effects + if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr)) + { + tokens.EndForceZeroConfidence(); + appearsDead = false; + } + + if (isSplit) + { +// const auto high = destExpr.GetHighExpr<HLIL_SPLIT>(); + const auto low = destExpr.GetLowExpr<HLIL_SPLIT>(); + + tokens.Append(OperationToken, "LOW"); + AppendSingleSizeToken(low.size, OperationToken, tokens); + tokens.AppendOpenParen(); + } + + if (assignUpdateOperator.has_value() && assignUpdateSource.has_value()) + { + if (assignUpdateNegate) + { + tokens.AppendIntegerTextToken(assignUpdateSource.value(), + -BNSignExtend( + assignUpdateSource.value().GetConstant<HLIL_CONST>(), assignUpdateSource.value().size, 8), + assignUpdateSource.value().size); + } + else + { + GetExprText(assignUpdateSource.value(), tokens, settings, asFullAst, AssignmentOperatorPrecedence, + InnerExpression, assignSignHint); + } + } + else + { + GetExprText(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence, InnerExpression, + assignSignHint); + } + + if (isSplit) + tokens.AppendCloseParen(); + + if (appearsDead) + tokens.EndForceZeroConfidence(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ASSIGN_UNPACK: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_ASSIGN_UNPACK>(); + const auto destExprs = instr.GetDestExprs<HLIL_ASSIGN_UNPACK>(); + const auto firstExpr = destExprs[0]; + + GetExprText(firstExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence); + tokens.Append(OperationToken, " = "); + GetExprText(srcExpr, tokens, settings, asFullAst, AssignmentOperatorPrecedence); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_STRUCT_FIELD: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_STRUCT_FIELD>(); + const auto fieldOffset = instr.GetOffset<HLIL_STRUCT_FIELD>(); + const auto memberIndex = instr.GetMemberIndex<HLIL_STRUCT_FIELD>(); + + const auto type = GetFieldType(srcExpr, false); + const auto fieldDisplayType = GetFieldDisplayType(type, fieldOffset, memberIndex, false); + if (fieldDisplayType == FieldDisplayOffset) + { + tokens.Append(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + tokens.AppendOpenParen(); + + GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "byte_offset"); + tokens.AppendOpenParen(); + tokens.AppendIntegerTextToken(instr, fieldOffset, instr.size); + tokens.AppendCloseParen(); + + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.Append(KeywordToken, " as "); + tokens.Append(TextToken, "*"); + Ref<Type> srcType = srcExpr.GetType(); + if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()) + tokens.Append(KeywordToken, "const "); + else + tokens.Append(KeywordToken, "mut "); + AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens); + tokens.AppendCloseParen(); + } + + char offsetStr[64]; + snprintf(offsetStr, sizeof(offsetStr), "0x%" PRIx64, fieldOffset); + + vector<string> nameList { offsetStr }; + HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( + GetFunction()->GetView(), type, srcExpr, nameList); + } + else if (fieldDisplayType == FieldDisplayMemberOffset) + { + tokens.Append(OperationToken, "*"); + BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence; + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + srcPrecedence = LowUnaryOperatorPrecedence; + } + GetExprText(srcExpr, tokens, settings, true, srcPrecedence); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.Append(KeywordToken, " as "); + tokens.Append(TextToken, "*"); + Ref<Type> srcType = srcExpr.GetType(); + if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()) + tokens.Append(KeywordToken, "const "); + else + tokens.Append(KeywordToken, "mut "); + AppendSizeToken(!instr.size ? srcExpr.size : instr.size, false, tokens); + tokens.AppendCloseParen(); + } + /* rest is rendered in AppendFieldTextTokens */ + } + else + { + GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + } + + AppendFieldTextTokens(srcExpr, fieldOffset, memberIndex, instr.size, tokens, false); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_DEREF: + [&]() { + auto srcExpr = instr.GetSourceExpr<HLIL_DEREF>(); + + auto appendMaybeBrace = [&](const InstructionTextToken& token) + { + if (token.type == BraceToken) + { + if (token.text == "(") + tokens.AppendOpenParen(); + else if (token.text == ")") + tokens.AppendCloseParen(); + else if (token.text == "[") + tokens.AppendOpenBracket(); + else if (token.text == "]") + tokens.AppendCloseBracket(); + else if (token.text == "{") + tokens.AppendOpenBrace(); + else if (token.text == "}") + tokens.AppendCloseBrace(); + else + tokens.Append(token); + } + else + { + tokens.Append(token); + } + }; + + if (srcExpr.operation == HLIL_CONST_PTR) + { + const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>(); + + const auto type = srcExpr.GetType(); + BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence; + if (type && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth() && + (!settings || settings->IsOptionSet(ShowTypeCasts))) + srcPrecedence = LowUnaryOperatorPrecedence; + + vector<InstructionTextToken> pointerTokens{}; + if (AppendPointerTextToken(srcExpr, constant, pointerTokens, settings, DereferenceNonDataSymbols, srcPrecedence) == DataSymbolResult) + { + if (type && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth()) + { + tokens.Append(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + tokens.AppendOpenParen(); + + for (const auto& token : pointerTokens) + { + appendMaybeBrace(token); + } + + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.Append(KeywordToken, " as "); + tokens.Append(TextToken, "*"); + Ref<Type> srcType = srcExpr.GetType(); + if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()) + tokens.Append(KeywordToken, "const "); + else + tokens.Append(KeywordToken, "mut "); + AppendSizeToken(instr.size, false, tokens); + tokens.AppendCloseParen(); + } + } + else + { + for (const auto& token : pointerTokens) + { + appendMaybeBrace(token); + } + } + } + else + { + for (const auto& token : pointerTokens) + { + appendMaybeBrace(token); + } + } + } + else + { + vector<bool> derefConst; + Ref<Type> srcType = srcExpr.GetType().GetValue(); + derefConst.push_back(srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()); + while (srcExpr.operation == HLIL_DEREF) + { + auto next = srcExpr.GetSourceExpr<HLIL_DEREF>(); + if (next.size == srcExpr.size) + { + srcType = srcExpr.GetType().GetValue(); + derefConst.push_back(srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()); + srcExpr = srcExpr.GetSourceExpr<HLIL_DEREF>(); + } + else + { + break; + } + } + + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + for (size_t index = 0; index < derefConst.size(); index++) + tokens.Append(OperationToken, "*"); + + BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence; + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.AppendOpenParen(); + srcPrecedence = LowUnaryOperatorPrecedence; + } + + GetExprText(srcExpr, tokens, settings, asFullAst, srcPrecedence); + + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.Append(KeywordToken, " as "); + for (auto isConst : derefConst) + { + tokens.Append(TextToken, "*"); + tokens.Append(KeywordToken, isConst ? "const ": "mut "); + } + AppendSizeToken(instr.size, false, tokens); + + tokens.AppendCloseParen(); + } + + if (parens) + tokens.AppendCloseParen(); + } + + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_TAILCALL: + [&]() { + const auto destExpr = instr.GetDestExpr<HLIL_TAILCALL>(); + const auto parameterExprs = instr.GetParameterExprs<HLIL_TAILCALL>(); + + tokens.Append(AnnotationToken, "/* tailcall */"); + tokens.NewLine(); + if (exprType != TrailingStatementExpression) + tokens.Append(KeywordToken, "return "); + GetExprText(destExpr, tokens, settings, asFullAst, MemberAndFunctionOperatorPrecedence); + tokens.AppendOpenParen(); + for (size_t index{}; index < parameterExprs.size(); index++) + { + const auto& parameterExpr = parameterExprs[index]; + if (index != 0) tokens.Append(TextToken, ", "); + GetExprText(parameterExpr, tokens, settings, asFullAst); + } + tokens.AppendCloseParen(); + if (exprType == StatementExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ADDRESS_OF: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_ADDRESS_OF>(); + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.Append(OperationToken, "&"); + GetExprText(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_E: + case HLIL_CMP_E: + [&]() { + bool parens = precedence > EqualityOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendComparison(" == ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_NE: + case HLIL_CMP_NE: + [&]() { + bool parens = precedence > EqualityOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendComparison(" != ", instr, tokens, settings, asFullAst, EqualityOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_LT: + case HLIL_CMP_SLT: + case HLIL_CMP_ULT: + [&]() { + bool parens = precedence > CompareOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> cmpSigned; + if (instr.operation == HLIL_CMP_ULT) + cmpSigned = false; + else if (instr.operation == HLIL_CMP_SLT) + cmpSigned = true; + AppendComparison(" < ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_LE: + case HLIL_CMP_SLE: + case HLIL_CMP_ULE: + [&]() { + bool parens = precedence > CompareOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> cmpSigned; + if (instr.operation == HLIL_CMP_ULE) + cmpSigned = false; + else if (instr.operation == HLIL_CMP_SLE) + cmpSigned = true; + AppendComparison(" <= ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_GE: + case HLIL_CMP_SGE: + case HLIL_CMP_UGE: + [&]() { + bool parens = precedence > CompareOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> cmpSigned; + if (instr.operation == HLIL_CMP_UGE) + cmpSigned = false; + else if (instr.operation == HLIL_CMP_SGE) + cmpSigned = true; + AppendComparison(" >= ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_GT: + case HLIL_CMP_SGT: + case HLIL_CMP_UGT: + [&]() { + bool parens = precedence > CompareOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> cmpSigned; + if (instr.operation == HLIL_CMP_UGT) + cmpSigned = false; + else if (instr.operation == HLIL_CMP_SGT) + cmpSigned = true; + AppendComparison(" > ", instr, tokens, settings, asFullAst, CompareOperatorPrecedence, cmpSigned); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + + case HLIL_AND: + [&]() { + bool parens = instr.size == 0 ? + precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalOrOperatorPrecedence : + precedence >= EqualityOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(instr.size == 0 ? " && " : " & ", instr, tokens, settings, asFullAst, + instr.size == 0 ? LogicalAndOperatorPrecedence : BitwiseAndOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_OR: + [&]() { + bool parens = (instr.size == 0) ? + precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalAndOperatorPrecedence : + precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(instr.size == 0 ? " || " : " | ", instr, tokens, settings, asFullAst, + instr.size == 0 ? LogicalOrOperatorPrecedence : BitwiseOrOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_XOR: + [&]() { + bool parens = precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || + precedence == BitwiseOrOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" ^ ", instr, tokens, settings, asFullAst, BitwiseXorOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ADC: + case HLIL_ADD_OVERFLOW: + case HLIL_FADD: + case HLIL_ADD: + [&]() { + const auto leftType = instr.GetLeftExpr().GetType(); + bool parens; + BNOperatorPrecedence opPrecedence = AddOperatorPrecedence; + if (leftType && leftType->IsPointer()) + { + parens = false; + opPrecedence = MemberAndFunctionOperatorPrecedence; + } + else + { + parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || + precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + } + + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" + ", instr, tokens, settings, asFullAst, opPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_SUB: + [&]{ + // Check for offset pointers + auto left = instr.GetLeftExpr<HLIL_SUB>(); + auto right = instr.GetRightExpr<HLIL_SUB>(); + if (left.operation == HLIL_VAR && right.operation == HLIL_CONST) + { + auto var = left.GetVariable<HLIL_VAR>(); + auto srcOffset = right.GetConstant<HLIL_CONST>(); + auto varType = GetFunction()->GetVariableType(var); + if (varType + && varType->GetClass() == PointerTypeClass + && varType->GetNamedTypeReference() + && varType->GetOffset() == srcOffset) + { + // Yes + tokens.Append(OperationToken, "ADJ"); + tokens.AppendOpenParen(); + GetExprText(left, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + tokens.AppendCloseParen(); + return; + } + } + + const auto leftType = instr.GetLeftExpr().GetType(); + bool parens; + BNOperatorPrecedence opPrecedence = SubOperatorPrecedence; + if (leftType && leftType->IsPointer()) + { + parens = false; + opPrecedence = MemberAndFunctionOperatorPrecedence; + } + else + { + parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || + precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + } + + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" - ", instr, tokens, settings, asFullAst, opPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + case HLIL_SBB: + case HLIL_FSUB: + [&]() { + bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || + precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" - ", instr, tokens, settings, asFullAst, SubOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_LSL: + [&]() { + bool parens = precedence > ShiftOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" << ", instr, tokens, settings, asFullAst, ShiftOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_LSR: + case HLIL_ASR: + [&]() { + bool parens = precedence > ShiftOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + AppendTwoOperand(" >> ", instr, tokens, settings, asFullAst, ShiftOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + + case HLIL_FMUL: + case HLIL_MUL: + case HLIL_MULU_DP: + case HLIL_MULS_DP: + [&]() { + bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence || + precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || + precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> mulSigned; + if (instr.operation == HLIL_MULU_DP) + mulSigned = false; + else if (instr.operation == HLIL_MULS_DP) + mulSigned = true; + AppendTwoOperand(" * ", instr, tokens, settings, asFullAst, MultiplyOperatorPrecedence, mulSigned); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FDIV: + case HLIL_DIVU: + case HLIL_DIVU_DP: + case HLIL_DIVS: + case HLIL_DIVS_DP: + [&]() { + bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence + || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence + || precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> divSigned; + if (instr.operation == HLIL_DIVU || instr.operation == HLIL_DIVU_DP) + divSigned = false; + else if (instr.operation == HLIL_DIVS || instr.operation == HLIL_DIVS_DP) + divSigned = true; + AppendTwoOperand(" / ", instr, tokens, settings, asFullAst, DivideOperatorPrecedence, divSigned); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_MODU: + case HLIL_MODU_DP: + case HLIL_MODS: + case HLIL_MODS_DP: + [&]() { + bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence + || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence + || precedence == BitwiseXorOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + std::optional<bool> modSigned; + if (instr.operation == HLIL_MODU || instr.operation == HLIL_MODU_DP) + modSigned = false; + else if (instr.operation == HLIL_MODS || instr.operation == HLIL_MODS_DP) + modSigned = true; + AppendTwoOperand(" % ", instr, tokens, settings, asFullAst, DivideOperatorPrecedence, modSigned); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + + case HLIL_ROR: + [&]() { + AppendTwoOperandMethodCall("rotate_right", instr, tokens, settings); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ROL: + [&]() { + AppendTwoOperandMethodCall("rotate_left", instr, tokens, settings); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_RLC: + [&]() { + AppendTwoOperandFunctionWithCarry("RLC", instr, tokens, settings); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_RRC: + [&]() { + AppendTwoOperandFunctionWithCarry("RRC", instr, tokens, settings); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_TEST_BIT: + [&]() { + AppendTwoOperandFunction("TEST_BIT", instr, tokens, settings); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FLOOR: + [&]() { + GetExprText(instr.GetSourceExpr<HLIL_FLOOR>(), tokens, settings, MemberAndFunctionOperatorPrecedence); + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "floor"); + tokens.AppendOpenParen(); + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_CEIL: + [&]() { + GetExprText(instr.GetSourceExpr<HLIL_CEIL>(), tokens, settings, MemberAndFunctionOperatorPrecedence); + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "ceil"); + tokens.AppendOpenParen(); + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FTRUNC: + [&]() { + GetExprText(instr.GetSourceExpr<HLIL_FTRUNC>(), tokens, settings, MemberAndFunctionOperatorPrecedence); + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "trunc"); + tokens.AppendOpenParen(); + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FABS: + [&]() { + GetExprText(instr.GetSourceExpr<HLIL_FABS>(), tokens, settings, MemberAndFunctionOperatorPrecedence); + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "abs"); + tokens.AppendOpenParen(); + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FSQRT: + [&]() { + GetExprText(instr.GetSourceExpr<HLIL_FSQRT>(), tokens, settings, MemberAndFunctionOperatorPrecedence); + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "sqrt"); + tokens.AppendOpenParen(); + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_ROUND_TO_INT: + [&]() { + GetExprText(instr.GetSourceExpr<HLIL_ROUND_TO_INT>(), tokens, settings, MemberAndFunctionOperatorPrecedence); + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "round"); + tokens.AppendOpenParen(); + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_O: + [&]() { + AppendTwoOperandFunction("FCMP_O", instr, tokens, settings, false); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FCMP_UO: + [&]() { + AppendTwoOperandFunction("FCMP_UO", instr, tokens, settings, false); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + + case HLIL_NOT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_NOT>(); + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.Append(OperationToken, "!"); + GetExprText(srcExpr, tokens, settings, UnaryOperatorPrecedence); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FNEG: + case HLIL_NEG: + [&]() { + const auto srcExpr = instr.GetSourceExpr(); + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + tokens.Append(OperationToken, "-"); + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, UnaryOperatorPrecedence, InnerExpression, true); + tokens.AppendCloseParen(); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FLOAT_CONV: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_CONV>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprText(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + const auto floatType = "f" + std::to_string(instr.size * 8); + + bool parens = precedence > LowUnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence); + tokens.Append(KeywordToken, " as "); + tokens.Append(TypeNameToken, floatType.c_str()); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_FLOAT_TO_INT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_FLOAT_TO_INT>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprText(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + + bool parens = precedence > LowUnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence); + tokens.Append(KeywordToken, " as "); + AppendSizeToken(instr.size, true, tokens); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_BOOL_TO_INT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_BOOL_TO_INT>(); + + bool parens = precedence > LowUnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence); + tokens.Append(KeywordToken, " as "); + AppendSizeToken(instr.size, true, tokens); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_INT_TO_FLOAT: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_INT_TO_FLOAT>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprText(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + const auto floatType = "f" + std::to_string(instr.size * 8); + + bool parens = precedence > LowUnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence); + tokens.Append(KeywordToken, " as "); + tokens.Append(TypeNameToken, floatType.c_str()); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_INTRINSIC: + [&]() { + const auto intrinsic = instr.GetIntrinsic<HLIL_INTRINSIC>(); + const auto intrinsicName = GetHighLevelILFunction()->GetArchitecture()->GetIntrinsicName(intrinsic); + const auto parameterExprs = instr.GetParameterExprs<HLIL_INTRINSIC>(); + + tokens.Append(KeywordToken, intrinsicName, intrinsic); + tokens.AppendOpenParen(); + for (size_t index{}; index < parameterExprs.size(); index++) + { + const auto& parameterExpr = parameterExprs[index]; + if (index != 0) tokens.Append(TextToken, ", "); + GetExprText(parameterExpr, tokens, settings, asFullAst); + } + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_RET: + [&]() { + const auto srcExprs = instr.GetSourceExprs<HLIL_RET>(); + + if (!asFullAst || exprType != TrailingStatementExpression) + tokens.Append(KeywordToken, "return"); + if (srcExprs.size() != 0) + { + if (!asFullAst || exprType != TrailingStatementExpression) + tokens.Append(TextToken, " "); + if (srcExprs.size() > 1) + tokens.AppendOpenParen(); + for (size_t index = 0; index < srcExprs.size(); index++) + { + const auto& srcExpr = srcExprs[index]; + if (index != 0) + tokens.Append(TextToken, ", "); + GetExprText(srcExpr, tokens, settings, asFullAst); + } + if (srcExprs.size() > 1) + tokens.AppendCloseParen(); + } + if (exprType == StatementExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_NORET: + [&]() { + tokens.Append(AnnotationToken, "/* no return */"); + }(); + break; + + case HLIL_UNREACHABLE: + [&]() { + tokens.Append(AnnotationToken, "/* unreachable */"); + }(); + break; + + case HLIL_JUMP: + [&]() { + const auto destExpr = instr.GetDestExpr<HLIL_JUMP>(); + tokens.Append(AnnotationToken, "/* jump -> "); + GetExprText(destExpr, tokens, settings); + tokens.Append(AnnotationToken, " */"); + }(); + break; + + case HLIL_UNDEF: + [&]() { + tokens.Append(AnnotationToken, "/* undefined */"); + }(); + break; + + case HLIL_TRAP: + [&]() { + const auto vector = instr.GetVector<HLIL_TRAP>(); + tokens.Append(KeywordToken, "trap"); + tokens.AppendOpenParen(); + tokens.AppendIntegerTextToken(instr, vector, 8); + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_DEREF_FIELD: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_DEREF_FIELD>(); + const auto offset = instr.GetOffset<HLIL_DEREF_FIELD>(); + const auto memberIndex = instr.GetMemberIndex<HLIL_DEREF_FIELD>(); + auto type = srcExpr.GetType().GetValue(); + + if (type && (type->GetClass() == PointerTypeClass)) + type = type->GetChildType().GetValue(); + + if (type && (type->GetClass() == NamedTypeReferenceClass)) + type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference()); + + bool derefOffset = false; + if (type && (type->GetClass() == StructureTypeClass)) + { + std::optional<size_t> memberIndexHint; + if (memberIndex != BN_INVALID_EXPR) + memberIndexHint = memberIndex; + + bool outer = true; + if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0, + [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset, + uint64_t adjustedOffset, const StructureMember& member) { + BNSymbolDisplayResult symbolType; + if (srcExpr.operation == HLIL_CONST_PTR) + { + const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>(); + symbolType = tokens.AppendPointerTextToken( + srcExpr, constant, settings, DisplaySymbolOnly, precedence); + } + else + { + GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + symbolType = OtherSymbolResult; + } + + const auto displayDeref = symbolType != DataSymbolResult; + if (displayDeref && outer) + tokens.Append(OperationToken, "->"); + else + tokens.Append(OperationToken, "."); + outer = false; + + vector<string> nameList {member.name}; + HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( + GetFunction()->GetView(), type, srcExpr, nameList); + + tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0, + BN_FULL_CONFIDENCE, nameList); + }), + memberIndexHint) + return; + } + else if (type && (type->GetClass() == StructureTypeClass)) + { + derefOffset = true; + } + + if (derefOffset || offset != 0) + { + bool parens = precedence > UnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + + tokens.Append(OperationToken, "*"); + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + tokens.AppendOpenParen(); + + if (srcExpr.operation == HLIL_CONST_PTR) + { + const auto constant = srcExpr.GetConstant<HLIL_CONST_PTR>(); + tokens.AppendPointerTextToken(srcExpr, constant, settings, DisplaySymbolOnly, precedence); + } + else + { + GetExprText(srcExpr, tokens, settings, true, MemberAndFunctionOperatorPrecedence); + } + + tokens.Append(TextToken, "."); + tokens.Append(OperationToken, "byte_offset"); + tokens.AppendOpenParen(); + tokens.AppendIntegerTextToken(instr, offset, instr.size); + tokens.AppendCloseParen(); + + if (!settings || settings->IsOptionSet(ShowTypeCasts)) + { + tokens.Append(KeywordToken, " as "); + tokens.Append(TextToken, "*"); + Ref<Type> srcType = srcExpr.GetType(); + if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()) + tokens.Append(KeywordToken, "const "); + else + tokens.Append(KeywordToken, "mut "); + AppendSizeToken(!derefOffset ? srcExpr.size : instr.size, true, tokens); + tokens.AppendCloseParen(); + } + + if (parens) + tokens.AppendCloseParen(); + } + + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_EXTERN_PTR: + [&]() { + const int64_t val = instr.GetOffset<HLIL_EXTERN_PTR>(); + if (val != 0) + tokens.AppendOpenParen(); + tokens.AppendPointerTextToken( + instr, instr.GetConstant<HLIL_EXTERN_PTR>(), settings, AddressOfDataSymbols, precedence); + if (val != 0) + { + char valStr[32]; + if (val >= 0) + { + tokens.Append(OperationToken, " + "); + if (val <= 9) + snprintf(valStr, sizeof(valStr), "%" PRIx64, val); + else + snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val); + } + else + { + tokens.Append(OperationToken, " - "); + if (val >= -9) + snprintf(valStr, sizeof(valStr), "%" PRIx64, -val); + else + snprintf(valStr, sizeof(valStr), "0x%" PRIx64, -val); + } + tokens.Append(IntegerToken, valStr, val); + tokens.AppendCloseParen(); + } + + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_SYSCALL: + [&]() { + tokens.Append(KeywordToken, "syscall"); + tokens.AppendOpenParen(); + const auto operandList = instr.GetParameterExprs<HLIL_SYSCALL>(); + vector<FunctionParameter> namedParams; + bool skipSyscallNumber = false; + if (GetFunction() && (operandList.size() > 0) && (operandList[0].operation == HLIL_CONST)) + { + const auto platform = GetFunction()->GetPlatform(); + if (platform) + { + const auto syscall = (uint32_t)operandList[0].GetConstant<HLIL_CONST>(); + const auto syscallName = platform->GetSystemCallName(syscall); + if (settings && settings->GetCallParameterHints() != NeverShowParameterHints) + { + const auto functionType = platform->GetSystemCallType(syscall); + if (functionType && (functionType->GetClass() == FunctionTypeClass)) + namedParams = functionType->GetParameters(); + } + if (syscallName.length()) + { + tokens.Append(TextToken, syscallName); + tokens.Append(TextToken, " "); + tokens.AppendOpenBrace(); + GetExprText(operandList[0], tokens, settings); + tokens.AppendCloseBrace(); + skipSyscallNumber = true; + } + } + } + for (size_t i = (skipSyscallNumber ? 1 : 0); i < operandList.size(); i++) + { + if (i != 0) + tokens.Append(TextToken, ", "); + GetExprText(operandList[i], tokens, settings); + } + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_BP: + [&]() { + tokens.Append(KeywordToken, "breakpoint"); + tokens.AppendOpenParen(); + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_UNIMPL_MEM: + case HLIL_UNIMPL: + [&]() { + const auto hlilFunc = GetHighLevelILFunction(); + const auto instructionText = hlilFunc->GetExprText(hlilFunc->GetInstruction( + hlilFunc->GetInstructionForExpr(instr.exprIndex)).exprIndex, true, settings); + tokens.Append(AnnotationToken, "/* "); + for (const auto& token : instructionText[0].tokens) + tokens.Append(token.type, token.text, token.value); + + if (instructionText.size() > 1) + tokens.Append(AnnotationToken, "..."); + + tokens.Append(AnnotationToken, " */"); + }(); + break; + + case HLIL_NOP: + [&]() { + tokens.Append(AnnotationToken, "/* nop */"); + }(); + break; + + case HLIL_GOTO: + [&]() { + const auto target = instr.GetTarget<HLIL_GOTO>(); + tokens.Append(KeywordToken, "goto "); + tokens.Append(GotoLabelToken, "'" + GetFunction()->GetGotoLabelName(target), target); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_LABEL: + [&]() { + const auto target = instr.GetTarget<HLIL_LABEL>(); + tokens.DecreaseIndent(); + tokens.Append(GotoLabelToken, "'" + GetFunction()->GetGotoLabelName(target), target); + tokens.Append(TextToken, ":"); + tokens.IncreaseIndent(); + }(); + break; + + case HLIL_LOW_PART: + [&]() { + const auto srcExpr = instr.GetSourceExpr<HLIL_LOW_PART>(); + if (settings && !settings->IsOptionSet(ShowTypeCasts)) + { + GetExprText(srcExpr, tokens, settings, asFullAst, precedence); + return; + } + bool parens = precedence > LowUnaryOperatorPrecedence; + if (parens) + tokens.AppendOpenParen(); + GetExprText(srcExpr, tokens, settings, asFullAst, LowUnaryOperatorPrecedence); + tokens.Append(KeywordToken, " as "); + AppendSizeToken(instr.size, signedHint.value_or(true), tokens); + if (parens) + tokens.AppendCloseParen(); + if (exprType != InnerExpression) + tokens.AppendSemicolon(); + }(); + break; + + case HLIL_SPLIT: break; + default: + [&]() { + char buf[64] {}; + snprintf(buf, sizeof(buf), "/* <UNIMPLEMENTED, %x> */", instr.operation); + tokens.Append(AnnotationToken, buf); + }(); + break; + } + + if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType()) + { + tokens.AppendCloseParen(); + } +} + + +void PseudoRustFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, + DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement) +{ + GetExprText(instr, tokens, settings, asFullAst, precedence, statement ? TrailingStatementExpression : InnerExpression); +} + + +string PseudoRustFunction::GetAnnotationStartString() const +{ + // Show annotations as Rust-style inline comments + return "/* "; +} + + +string PseudoRustFunction::GetAnnotationEndString() const +{ + // Show annotations as Rust-style inline comments + return " */"; +} + + +PseudoRustFunctionType::PseudoRustFunctionType(): LanguageRepresentationFunctionType("Pseudo Rust") +{ + // Create a type printer for Rust-style types and register it + m_typePrinter = new RustTypePrinter(); + TypePrinter::Register(m_typePrinter); +} + + +Ref<LanguageRepresentationFunction> PseudoRustFunctionType::Create(Architecture* arch, Function* owner, + HighLevelILFunction* highLevelILFunction) +{ + return new PseudoRustFunction(arch, owner, highLevelILFunction); +} + + +Ref<TypePrinter> PseudoRustFunctionType::GetTypePrinter() +{ + // Return the Rust type printer as the default type printer for this language + return m_typePrinter; +} + + +vector<DisassemblyTextLine> PseudoRustFunctionType::GetFunctionTypeTokens(Function* func, DisassemblySettings* settings) +{ + vector<DisassemblyTextLine> result; + DisassemblyTextLine line; + line.addr = func->GetStart(); + + RustTypePrinter printer; + Ref<Type> funcType = func->GetType(); + if (!funcType) + return {}; + + // Use the Rust type printer to generate a Rust formatted function declaration + vector<InstructionTextToken> before = printer.GetTypeTokensBeforeName(funcType, func->GetPlatform()); + vector<InstructionTextToken> after = printer.GetTypeTokensAfterNameInternal(funcType, func->GetPlatform(), + BN_FULL_CONFIDENCE, nullptr, NoTokenEscapingType, true); + + line.tokens = before; + if (!before.empty()) + line.tokens.emplace_back(TextToken, " "); + + Ref<Symbol> sym = func->GetSymbol(); + line.tokens.emplace_back(CodeSymbolToken, sym->GetShortName(), func->GetStart()); + + line.tokens.insert(line.tokens.end(), after.begin(), after.end()); + return {line}; +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifndef DEMO_VERSION + BINARYNINJAPLUGIN void CorePluginDependencies() + { + } +#endif + +#ifdef DEMO_VERSION + bool PseudoCPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + LanguageRepresentationFunctionType* type = new PseudoRustFunctionType(); + LanguageRepresentationFunctionType::Register(type); + return true; + } +} diff --git a/lang/rust/pseudorust.h b/lang/rust/pseudorust.h new file mode 100644 index 00000000..a6494319 --- /dev/null +++ b/lang/rust/pseudorust.h @@ -0,0 +1,79 @@ +#pragma once + +#include "binaryninjaapi.h" + +class PseudoRustFunction: public BinaryNinja::LanguageRepresentationFunction +{ + BinaryNinja::Ref<BinaryNinja::HighLevelILFunction> m_highLevelIL; + + enum FieldDisplayType + { + FieldDisplayName, + FieldDisplayOffset, + FieldDisplayMemberOffset, + FieldDisplayNone + }; + + enum ExpressionType + { + StatementExpression, + TrailingStatementExpression, + InnerExpression + }; + + BinaryNinja::Ref<BinaryNinja::Type> GetFieldType(const BinaryNinja::HighLevelILInstruction& var, bool deref); + FieldDisplayType GetFieldDisplayType(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t offset, size_t memberIndex, bool deref); + + BNSymbolDisplayResult AppendPointerTextToken(const BinaryNinja::HighLevelILInstruction& instr, int64_t val, + std::vector<BinaryNinja::InstructionTextToken>& tokens, BinaryNinja::DisassemblySettings* settings, + BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence); + std::string GetSizeToken(size_t size, bool isSigned); + void AppendSizeToken(size_t size, bool isSigned, BinaryNinja::HighLevelILTokenEmitter& emitter); + void AppendSingleSizeToken(size_t size, BNInstructionTextTokenType type, BinaryNinja::HighLevelILTokenEmitter& emitter); + void AppendComparison(const std::string& comparison, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst, + BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt); + void AppendTwoOperand(const std::string& operand, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& emitter, BinaryNinja::DisassemblySettings* settings, bool asFullAst, + BNOperatorPrecedence precedence, std::optional<bool> signedHint = std::nullopt); + void AppendTwoOperandFunction(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, bool sizeToken = true); + void AppendTwoOperandMethodCall(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings); + void AppendTwoOperandFunctionWithCarry(const std::string& function, const BinaryNinja::HighLevelILInstruction& instr, + BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings); + void AppendFieldTextTokens(const BinaryNinja::HighLevelILInstruction& var, uint64_t offset, size_t memberIndex, size_t size, + BinaryNinja::HighLevelILTokenEmitter& tokens, bool deref); + bool IsMutable(const BinaryNinja::Variable& var) const; + + void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens, + BinaryNinja::DisassemblySettings* settings, bool asFullAst = true, + BNOperatorPrecedence precedence = TopLevelOperatorPrecedence, ExpressionType exprType = InnerExpression, + std::optional<bool> signedHint = std::nullopt); + +protected: + virtual void InitTokenEmitter(BinaryNinja::HighLevelILTokenEmitter& tokens) override; + virtual void GetExprText(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens, + BinaryNinja::DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement) override; + virtual void BeginLines(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override; + virtual void EndLines(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override; + +public: + PseudoRustFunction(BinaryNinja::Architecture* arch, BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction); + + std::string GetAnnotationStartString() const override; + std::string GetAnnotationEndString() const override; +}; + +class PseudoRustFunctionType: public BinaryNinja::LanguageRepresentationFunctionType +{ + BinaryNinja::Ref<BinaryNinja::TypePrinter> m_typePrinter; + +public: + PseudoRustFunctionType(); + BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch, + BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override; + BinaryNinja::Ref<BinaryNinja::TypePrinter> GetTypePrinter() override; + std::vector<BinaryNinja::DisassemblyTextLine> GetFunctionTypeTokens(BinaryNinja::Function* func, + BinaryNinja::DisassemblySettings* settings) override; +}; diff --git a/lang/rust/rusttypes.cpp b/lang/rust/rusttypes.cpp new file mode 100644 index 00000000..c43817ab --- /dev/null +++ b/lang/rust/rusttypes.cpp @@ -0,0 +1,339 @@ +#include <inttypes.h> +#include "rusttypes.h" + +using namespace std; +using namespace BinaryNinja; + + +RustTypePrinter::RustTypePrinter(): TypePrinter("RustTypePrinter") +{ +} + + +void RustTypePrinter::AppendCallingConventionTokens(Type* type, Platform* platform, uint8_t baseConfidence, + vector<InstructionTextToken>& tokens) +{ + if (type->GetCallingConvention() && platform && + type->GetCallingConvention().GetValue() != platform->GetDefaultCallingConvention()) + { + uint8_t ccConfidence = type->GetCallingConvention().GetCombinedConfidence(baseConfidence); + tokens.emplace_back(baseConfidence, KeywordToken, "extern"); + tokens.emplace_back(ccConfidence, TextToken, " "); + tokens.emplace_back(ccConfidence, BraceToken, "\""); + if (type->GetCallingConvention().GetValue() == platform->GetCdeclCallingConvention()) + { + tokens.emplace_back(StringToken, StringDisplayTokenContext, "C", 0, + Utf8String, 0, BN_INVALID_OPERAND, ccConfidence); + } + else + { + tokens.emplace_back(StringToken, StringDisplayTokenContext, type->GetCallingConvention()->GetName(), 0, + Utf8String, 0, BN_INVALID_OPERAND, ccConfidence); + } + tokens.emplace_back(ccConfidence, BraceToken, "\""); + tokens.emplace_back(ccConfidence, TextToken, " "); + } +} + + +void RustTypePrinter::GetStructureMemberTokens(Platform* platform, Type* type, uint8_t baseConfidence, + BNTokenEscapingType escaping, vector<InstructionTextToken>& out) +{ + uint64_t offset = 0; + vector<StructureMember> members = type->GetStructure()->GetMembers(); + for (size_t i = 0; i < members.size(); i++) + { + if (members[i].type->GetClass() == FunctionTypeClass) + continue; + if (i != 0) + out.emplace_back(baseConfidence, TextToken, ", "); + else + out.emplace_back(baseConfidence, TextToken, " "); + + if ((!type->GetStructure()->IsPacked()) && (members[i].type->GetAlignment() != 0) && + ((offset % members[i].type->GetAlignment()) != 0)) + offset += members[i].type->GetAlignment() - (offset % members[i].type->GetAlignment()); + + if (members[i].offset < offset) + { + out.emplace_back(baseConfidence, KeywordToken, "__offset"); + out.emplace_back(baseConfidence, BraceToken, "("); + out.emplace_back(IntegerToken, DisassemblyTextRenderer::GetDisplayStringForInteger( + nullptr, UnsignedHexadecimalDisplayType, members[i].offset, 8), + members[i].offset, 0, BN_INVALID_OPERAND, baseConfidence); + out.emplace_back(baseConfidence, BraceToken, ")"); + offset = members[i].offset; + } + else if (members[i].offset > offset) + { + char offsetStr[32]; + snprintf(offsetStr, sizeof(offsetStr), "_%" PRIx64, offset); + out.emplace_back(baseConfidence, KeywordToken, "__padding"); + out.emplace_back(baseConfidence, TextToken, " "); + out.emplace_back(baseConfidence, TextToken, offsetStr); + out.emplace_back(baseConfidence, TextToken, ": "); + out.emplace_back(baseConfidence, BraceToken, "["); + out.emplace_back(baseConfidence, KeywordToken, "u8"); + out.emplace_back(baseConfidence, TextToken, "; "); + out.emplace_back(IntegerToken, DisassemblyTextRenderer::GetDisplayStringForInteger( + nullptr, UnsignedHexadecimalDisplayType, members[i].offset - offset, 8), + members[i].offset - offset, 0, BN_INVALID_OPERAND, baseConfidence); + out.emplace_back(baseConfidence, BraceToken, "]"); + offset = members[i].offset; + } + + vector<InstructionTextToken> after = + GetTypeTokensAfterName(members[i].type, platform, BN_FULL_CONFIDENCE, nullptr, escaping); + + out.emplace_back(baseConfidence, FieldNameToken, + NameList::EscapeTypeName(members[i].name, escaping)); + out.insert(out.end(), after.begin(), after.end()); + + if (!type->GetStructure()->IsUnion()) + offset = members[i].offset + members[i].type->GetWidth(); + } + out.emplace_back(baseConfidence, TextToken, " "); +} + + +vector<InstructionTextToken> RustTypePrinter::GetTypeTokensBeforeName( + Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType, + BNTokenEscapingType escaping) +{ + vector<InstructionTextToken> tokens; + if ((!parentType || !parentType->IsPointer()) && type->GetClass() == FunctionTypeClass) + { + AppendCallingConventionTokens(type, platform, baseConfidence, tokens); + tokens.emplace_back(baseConfidence, KeywordToken, "fn"); + } + return tokens; +} + + +vector<InstructionTextToken> RustTypePrinter::GetTypeTokensAfterName( + Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType, + BNTokenEscapingType escaping) +{ + return GetTypeTokensAfterNameInternal(type, platform, baseConfidence, parentType, escaping); +} + + +vector<InstructionTextToken> RustTypePrinter::GetTypeTokensAfterNameInternal( + Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType, + BNTokenEscapingType escaping, bool functionHeader) +{ + vector<InstructionTextToken> tokens; + if (!parentType && type->GetClass() != FunctionTypeClass) + tokens.emplace_back(TextToken, ": "); + + switch (type->GetClass()) + { + case FunctionTypeClass: + { + if (parentType && parentType->IsPointer()) + { + AppendCallingConventionTokens(type, platform, baseConfidence, tokens); + tokens.emplace_back(baseConfidence, KeywordToken, "fn"); + } + + tokens.emplace_back(baseConfidence, BraceToken, "("); + + auto params = type->GetParameters(); + for (size_t i = 0; i < params.size(); i++) + { + if (i != 0) + tokens.emplace_back(baseConfidence, TextToken, ", "); + + vector<InstructionTextToken> paramTokens = GetTypeTokensAfterName(params[i].type, platform, + params[i].type.GetCombinedConfidence(baseConfidence), type, escaping); + + if (functionHeader) + { + for (auto& token : paramTokens) + { + token.context = LocalVariableTokenContext; + token.address = params[i].location.ToIdentifier(); + } + } + + InstructionTextToken nameToken; + if (params[i].name.length() == 0) + { + nameToken = InstructionTextToken(TextToken, "_"); + } + else + { + nameToken = InstructionTextToken(ArgumentNameToken, NameList::EscapeTypeName(params[i].name, escaping), i); + if (functionHeader) + { + nameToken.context = LocalVariableTokenContext; + nameToken.address = params[i].location.ToIdentifier(); + } + } + tokens.push_back(nameToken); + tokens.emplace_back(TextToken, ": "); + tokens.insert(tokens.end(), paramTokens.begin(), paramTokens.end()); + + if (!params[i].defaultLocation && platform) + { + switch (params[i].location.type) + { + case RegisterVariableSourceType: + { + string registerName = platform->GetArchitecture()->GetRegisterName((uint32_t)params[i].location.storage); + tokens.emplace_back(TextToken, " @ "); + tokens.emplace_back(RegisterToken, NameList::EscapeTypeName(registerName, escaping), params[i].location.storage); + break; + } + case FlagVariableSourceType: + { + string flagName = platform->GetArchitecture()->GetFlagName((uint32_t)params[i].location.storage); + tokens.emplace_back(TextToken, " @ "); + tokens.emplace_back(AnnotationToken, NameList::EscapeTypeName(flagName, escaping), params[i].location.storage); + break; + } + case StackVariableSourceType: + { + tokens.emplace_back(TextToken, " @ "); + char storageStr[32]; + snprintf(storageStr, sizeof(storageStr), "%" PRIi64, params[i].location.storage); + tokens.emplace_back(IntegerToken, storageStr, params[i].location.storage); + break; + } + } + } + } + + if (type->HasVariableArguments()) + { + if (params.size() != 0) + tokens.emplace_back(TextToken, ", "); + tokens.emplace_back(type->HasVariableArguments().GetCombinedConfidence(baseConfidence), TextToken, "..."); + } + + tokens.emplace_back(baseConfidence, BraceToken, ")"); + + if (!type->CanReturn()) + { + // No return functions are part of the Rust language, marked by returning the "never" type. + tokens.emplace_back(baseConfidence, TextToken, " -> "); + tokens.emplace_back(type->CanReturn().GetCombinedConfidence(baseConfidence), TextToken, "!"); + } + else if (type->GetChildType() && type->GetChildType()->GetClass() != VoidTypeClass) + { + tokens.emplace_back(baseConfidence, TextToken, " -> "); + vector<InstructionTextToken> retn = GetTypeTokensAfterName(type->GetChildType(), platform, + type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping); + if (functionHeader) + { + for (auto& i : retn) + i.context = FunctionReturnTokenContext; + } + tokens.insert(tokens.end(), retn.begin(), retn.end()); + } + break; + } + case IntegerTypeClass: + tokens.emplace_back(baseConfidence, TypeNameToken, + (type->IsSigned() ? "i" : "u") + to_string(type->GetWidth() * 8)); + break; + case FloatTypeClass: + tokens.emplace_back(baseConfidence, TypeNameToken, "f" + to_string(type->GetWidth() * 8)); + break; + case WideCharTypeClass: + if (type->GetWidth() == 4) + tokens.emplace_back(baseConfidence, TypeNameToken, "char"); + else + tokens.emplace_back(baseConfidence, TypeNameToken, "char" + to_string(type->GetWidth() * 8)); + break; + case BoolTypeClass: + tokens.emplace_back(baseConfidence, TypeNameToken, "bool"); + break; + case VoidTypeClass: + if (parentType && parentType->IsPointer()) + { + tokens.emplace_back(baseConfidence, TypeNameToken, "c_void"); + } + else + { + tokens.emplace_back(baseConfidence, BraceToken, "("); + tokens.emplace_back(baseConfidence, BraceToken, ")"); + } + break; + case StructureTypeClass: + if (type->GetRegisteredName()) + { + tokens.emplace_back(baseConfidence, TypeNameToken, type->GetRegisteredName()->GetName().GetString(escaping)); + } + else + { + tokens.emplace_back(baseConfidence, KeywordToken, "struct"); + tokens.emplace_back(baseConfidence, TextToken, " "); + tokens.emplace_back(baseConfidence, BraceToken, "{"); + GetStructureMemberTokens(platform, type, baseConfidence, escaping, tokens); + tokens.emplace_back(baseConfidence, BraceToken, "}"); + } + break; + case EnumerationTypeClass: + if (type->GetRegisteredName()) + tokens.emplace_back(baseConfidence, TypeNameToken, type->GetRegisteredName()->GetName().GetString(escaping)); + else + tokens.emplace_back(baseConfidence, TypeNameToken, "i" + to_string(type->GetWidth() * 8)); + break; + case VarArgsTypeClass: + tokens.emplace_back(baseConfidence, TextToken, "..."); + break; + case PointerTypeClass: + { + if (type->GetChildType()->GetClass() == FunctionTypeClass) + { + vector<InstructionTextToken> inner = GetTypeTokensAfterName( + type->GetChildType(), platform, baseConfidence, type, escaping); + tokens.insert(tokens.end(), inner.begin(), inner.end()); + return tokens; + } + + tokens.emplace_back(baseConfidence, TextToken, "*"); + if (type->GetChildType()->IsConst()) + tokens.emplace_back(baseConfidence, KeywordToken, "const"); + else + tokens.emplace_back(baseConfidence, KeywordToken, "mut"); + tokens.emplace_back(baseConfidence, TextToken, " "); + vector<InstructionTextToken> inner = GetTypeTokensAfterName(type->GetChildType(), platform, + type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping); + tokens.insert(tokens.end(), inner.begin(), inner.end()); + break; + } + case ArrayTypeClass: + { + tokens.emplace_back(baseConfidence, BraceToken, "["); + vector<InstructionTextToken> inner = GetTypeTokensAfterName(type->GetChildType(), platform, + type->GetChildType().GetCombinedConfidence(baseConfidence), type, escaping); + tokens.insert(tokens.end(), inner.begin(), inner.end()); + tokens.emplace_back(baseConfidence, TextToken, "; "); + tokens.emplace_back(ArrayIndexToken, DisassemblyTextRenderer::GetDisplayStringForInteger( + nullptr, type->GetIntegerTypeDisplayType(), type->GetElementCount(), 8).c_str(), type->GetElementCount()); + tokens.emplace_back(baseConfidence, BraceToken, "]"); + } + case ValueTypeClass: + tokens.emplace_back(baseConfidence, TextToken, NameList::EscapeTypeName(type->GetAlternateName(), escaping)); + break; + case NamedTypeReferenceClass: + tokens.emplace_back(baseConfidence, TypeNameToken, type->GetNamedTypeReference()->GetName().GetString(escaping)); + break; + default: + tokens.emplace_back(AnnotationToken, "/* invalid type */"); + break; + } + + return tokens; +} + + +vector<TypeDefinitionLine> RustTypePrinter::GetTypeLines( + Ref<Type> type, const TypeContainer& types, const QualifiedName& name, int paddingCols, + bool collapsed, BNTokenEscapingType escaping) +{ + // TODO: Implement this to get type rendering in the Types view + return {}; +} diff --git a/lang/rust/rusttypes.h b/lang/rust/rusttypes.h new file mode 100644 index 00000000..419e5253 --- /dev/null +++ b/lang/rust/rusttypes.h @@ -0,0 +1,32 @@ +#pragma once + +#include "binaryninjaapi.h" + +class RustTypePrinter: public BinaryNinja::TypePrinter +{ + void AppendCallingConventionTokens(BinaryNinja::Type* type, BinaryNinja::Platform* platform, uint8_t baseConfidence, + std::vector<BinaryNinja::InstructionTextToken>& tokens); + void GetStructureMemberTokens(BinaryNinja::Platform* platform, BinaryNinja::Type* type, uint8_t baseConfidence, + BNTokenEscapingType escaping, std::vector<BinaryNinja::InstructionTextToken>& out); + +public: + RustTypePrinter(); + + std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensBeforeName( + BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform, + uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr, + BNTokenEscapingType escaping = NoTokenEscapingType) override; + std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensAfterName( + BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform, + uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr, + BNTokenEscapingType escaping = NoTokenEscapingType) override; + std::vector<BinaryNinja::TypeDefinitionLine> GetTypeLines( + BinaryNinja::Ref<BinaryNinja::Type> type, const BinaryNinja::TypeContainer& types, + const BinaryNinja::QualifiedName& name, int paddingCols = 64, bool collapsed = false, + BNTokenEscapingType escaping = NoTokenEscapingType) override; + + std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensAfterNameInternal( + BinaryNinja::Ref<BinaryNinja::Type> type, BinaryNinja::Ref<BinaryNinja::Platform> platform, + uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr, + BNTokenEscapingType escaping = NoTokenEscapingType, bool functionHeader = false); +}; diff --git a/languagerepresentation.cpp b/languagerepresentation.cpp index 62f592e8..906b4901 100644 --- a/languagerepresentation.cpp +++ b/languagerepresentation.cpp @@ -1,15 +1,493 @@ #include "binaryninjaapi.h" +#include "highlevelilinstruction.h" using namespace BinaryNinja; using namespace std; -LanguageRepresentationFunction::LanguageRepresentationFunction(Architecture* arch, Function* func) +LanguageRepresentationFunction::LanguageRepresentationFunction(Architecture* arch, Function* func, HighLevelILFunction* highLevelIL) { - m_object = BNCreateLanguageRepresentationFunction(arch->GetObject(), func ? func->GetObject() : nullptr); + BNCustomLanguageRepresentationFunction callbacks; + callbacks.context = this; + callbacks.freeObject = FreeCallback; + callbacks.externalRefTaken = nullptr; + callbacks.externalRefReleased = nullptr; + callbacks.initTokenEmitter = InitTokenEmitterCallback; + callbacks.getExprText = GetExprTextCallback; + callbacks.beginLines = BeginLinesCallback; + callbacks.endLines = EndLinesCallback; + callbacks.getCommentStartString = GetCommentStartStringCallback; + callbacks.getCommentEndString = GetCommentEndStringCallback; + callbacks.getAnnotationStartString = GetAnnotationStartStringCallback; + callbacks.getAnnotationEndString = GetAnnotationEndStringCallback; + AddRefForRegistration(); + m_object = BNCreateCustomLanguageRepresentationFunction(arch->GetObject(), func->GetObject(), + highLevelIL->GetObject(), &callbacks); } LanguageRepresentationFunction::LanguageRepresentationFunction(BNLanguageRepresentationFunction* func) { m_object = func; -}
\ No newline at end of file +} + + +vector<DisassemblyTextLine> LanguageRepresentationFunction::GetExprText(const HighLevelILInstruction& instr, + DisassemblySettings* settings, bool asFullAst, BNOperatorPrecedence precedence, bool statement) +{ + size_t count = 0; + BNDisassemblyTextLine* lines = BNGetLanguageRepresentationFunctionExprText(m_object, + instr.function->GetObject(), instr.exprIndex, settings ? settings->GetObject() : nullptr, + asFullAst, precedence, statement, &count); + + vector<DisassemblyTextLine> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + DisassemblyTextLine line; + line.addr = lines[i].addr; + line.instrIndex = lines[i].instrIndex; + line.highlight = lines[i].highlight; + line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count); + line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount); + result.push_back(line); + } + + BNFreeDisassemblyTextLines(lines, count); + return result; +} + + +vector<DisassemblyTextLine> LanguageRepresentationFunction::GetLinearLines( + const HighLevelILInstruction& instr, DisassemblySettings* settings, bool asFullAst) +{ + size_t count = 0; + BNDisassemblyTextLine* lines = BNGetLanguageRepresentationFunctionLinearLines(m_object, instr.function->GetObject(), + instr.exprIndex, settings ? settings->GetObject() : nullptr, asFullAst, &count); + + vector<DisassemblyTextLine> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + DisassemblyTextLine line; + line.addr = lines[i].addr; + line.instrIndex = lines[i].instrIndex; + line.highlight = lines[i].highlight; + line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count); + line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount); + result.push_back(line); + } + + BNFreeDisassemblyTextLines(lines, count); + return result; +} + + +vector<DisassemblyTextLine> LanguageRepresentationFunction::GetBlockLines( + BasicBlock* block, DisassemblySettings* settings) +{ + size_t count = 0; + BNDisassemblyTextLine* lines = BNGetLanguageRepresentationFunctionBlockLines( + m_object, block->GetObject(), settings ? settings->GetObject() : nullptr, &count); + + vector<DisassemblyTextLine> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + DisassemblyTextLine line; + line.addr = lines[i].addr; + line.instrIndex = lines[i].instrIndex; + line.highlight = lines[i].highlight; + line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count); + line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount); + result.push_back(line); + } + + BNFreeDisassemblyTextLines(lines, count); + return result; +} + + +BNHighlightColor LanguageRepresentationFunction::GetHighlight(BasicBlock* block) +{ + return BNGetLanguageRepresentationFunctionHighlight(m_object, block->GetObject()); +} + + +Ref<Architecture> LanguageRepresentationFunction::GetArchitecture() const +{ + return new CoreArchitecture(BNGetLanguageRepresentationArchitecture(m_object)); +} + + +Ref<Function> LanguageRepresentationFunction::GetFunction() const +{ + return new Function(BNGetLanguageRepresentationOwnerFunction(m_object)); +} + + +Ref<HighLevelILFunction> LanguageRepresentationFunction::GetHighLevelILFunction() const +{ + return new HighLevelILFunction(BNGetLanguageRepresentationILFunction(m_object)); +} + + +void LanguageRepresentationFunction::InitTokenEmitter(HighLevelILTokenEmitter&) +{ +} + + +void LanguageRepresentationFunction::BeginLines(const HighLevelILInstruction&, HighLevelILTokenEmitter&) +{ +} + + +void LanguageRepresentationFunction::EndLines(const HighLevelILInstruction&, HighLevelILTokenEmitter&) +{ +} + + +void LanguageRepresentationFunction::FreeCallback(void* ctxt) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + func->ReleaseForRegistration(); +} + + +void LanguageRepresentationFunction::InitTokenEmitterCallback(void* ctxt, BNHighLevelILTokenEmitter* tokens) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens)); + func->InitTokenEmitter(*tokenObj); +} + + +void LanguageRepresentationFunction::GetExprTextCallback(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, + BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, bool asFullAst, + BNOperatorPrecedence precedence, bool statement) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + Ref<HighLevelILFunction> ilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(il)); + HighLevelILInstruction instr = ilObj->GetExpr(exprIndex); + Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens)); + Ref<DisassemblySettings> settingsObj = settings ? new DisassemblySettings(BNNewDisassemblySettingsReference(settings)) : nullptr; + func->GetExprText(instr, *tokenObj, settingsObj, asFullAst, precedence, statement); +} + + +void LanguageRepresentationFunction::BeginLinesCallback(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, + BNHighLevelILTokenEmitter* tokens) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + Ref<HighLevelILFunction> ilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(il)); + HighLevelILInstruction instr = ilObj->GetExpr(exprIndex); + Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens)); + func->BeginLines(instr, *tokenObj); +} + + +void LanguageRepresentationFunction::EndLinesCallback(void* ctxt, BNHighLevelILFunction* il, size_t exprIndex, + BNHighLevelILTokenEmitter* tokens) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + Ref<HighLevelILFunction> ilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(il)); + HighLevelILInstruction instr = ilObj->GetExpr(exprIndex); + Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens)); + func->EndLines(instr, *tokenObj); +} + + +char* LanguageRepresentationFunction::GetCommentStartStringCallback(void* ctxt) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + return BNAllocString(func->GetCommentStartString().c_str()); +} + + +char* LanguageRepresentationFunction::GetCommentEndStringCallback(void* ctxt) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + return BNAllocString(func->GetCommentEndString().c_str()); +} + + +char* LanguageRepresentationFunction::GetAnnotationStartStringCallback(void* ctxt) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + return BNAllocString(func->GetAnnotationStartString().c_str()); +} + + +char* LanguageRepresentationFunction::GetAnnotationEndStringCallback(void* ctxt) +{ + LanguageRepresentationFunction* func = (LanguageRepresentationFunction*)ctxt; + return BNAllocString(func->GetAnnotationEndString().c_str()); +} + + +CoreLanguageRepresentationFunction::CoreLanguageRepresentationFunction(BNLanguageRepresentationFunction* func): + LanguageRepresentationFunction(func) +{ +} + + +void CoreLanguageRepresentationFunction::GetExprText(const HighLevelILInstruction&, HighLevelILTokenEmitter&, + DisassemblySettings*, bool, BNOperatorPrecedence, bool statement) +{ +} + + +string CoreLanguageRepresentationFunction::GetCommentStartString() const +{ + char* result = BNGetLanguageRepresentationFunctionCommentStartString(m_object); + string resultStr(result); + BNFreeString(result); + return resultStr; +} + + +string CoreLanguageRepresentationFunction::GetCommentEndString() const +{ + char* result = BNGetLanguageRepresentationFunctionCommentEndString(m_object); + string resultStr(result); + BNFreeString(result); + return resultStr; +} + + +string CoreLanguageRepresentationFunction::GetAnnotationStartString() const +{ + char* result = BNGetLanguageRepresentationFunctionAnnotationStartString(m_object); + string resultStr(result); + BNFreeString(result); + return resultStr; +} + + +string CoreLanguageRepresentationFunction::GetAnnotationEndString() const +{ + char* result = BNGetLanguageRepresentationFunctionAnnotationEndString(m_object); + string resultStr(result); + BNFreeString(result); + return resultStr; +} + + +LanguageRepresentationFunctionType::LanguageRepresentationFunctionType(const std::string& name): m_nameForRegister(name) +{ + m_object = nullptr; +} + + +LanguageRepresentationFunctionType::LanguageRepresentationFunctionType(BNLanguageRepresentationFunctionType* type) +{ + m_object = type; +} + + +string LanguageRepresentationFunctionType::GetName() const +{ + char* name = BNGetLanguageRepresentationFunctionTypeName(m_object); + string result = name; + BNFreeString(name); + return result; +} + + +bool LanguageRepresentationFunctionType::IsValid(BinaryView*) +{ + return true; +} + + +vector<DisassemblyTextLine> LanguageRepresentationFunctionType::GetFunctionTypeTokens(Function*, DisassemblySettings*) +{ + return {}; +} + + +void LanguageRepresentationFunctionType::Register(LanguageRepresentationFunctionType* type) +{ + BNCustomLanguageRepresentationFunctionType callbacks; + callbacks.context = type; + callbacks.create = CreateCallback; + callbacks.isValid = IsValidCallback; + callbacks.getTypePrinter = GetTypePrinterCallback; + callbacks.getTypeParser = GetTypeParserCallback; + callbacks.getFunctionTypeTokens = GetFunctionTypeTokensCallback; + callbacks.freeLines = FreeLinesCallback; + + type->AddRefForRegistration(); + type->m_object = + BNRegisterLanguageRepresentationFunctionType(type->m_nameForRegister.c_str(), &callbacks); +} + + +BNLanguageRepresentationFunction* LanguageRepresentationFunctionType::CreateCallback( + void* ctxt, BNArchitecture* arch, BNFunction* owner, BNHighLevelILFunction* highLevelIL) +{ + LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt; + Ref<Architecture> archObj = new CoreArchitecture(arch); + Ref<Function> ownerObj = new Function(BNNewFunctionReference(owner)); + Ref<HighLevelILFunction> il = new HighLevelILFunction(BNNewHighLevelILFunctionReference(highLevelIL)); + Ref<LanguageRepresentationFunction> result = type->Create(archObj, ownerObj, il); + if (!result) + return nullptr; + return BNNewLanguageRepresentationFunctionReference(result->GetObject()); +} + + +bool LanguageRepresentationFunctionType::IsValidCallback(void* ctxt, BNBinaryView* view) +{ + LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt; + Ref<BinaryView> viewObj = new BinaryView(BNNewViewReference(view)); + return type->IsValid(viewObj); +} + + +BNTypePrinter* LanguageRepresentationFunctionType::GetTypePrinterCallback(void* ctxt) +{ + LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt; + Ref<TypePrinter> result = type->GetTypePrinter(); + if (!result) + return nullptr; + return result->GetObject(); +} + + +BNTypeParser* LanguageRepresentationFunctionType::GetTypeParserCallback(void* ctxt) +{ + LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt; + Ref<TypeParser> result = type->GetTypeParser(); + if (!result) + return nullptr; + return result->GetObject(); +} + + +BNDisassemblyTextLine* LanguageRepresentationFunctionType::GetFunctionTypeTokensCallback( + void* ctxt, BNFunction* func, BNDisassemblySettings* settings, size_t* count) +{ + LanguageRepresentationFunctionType* type = (LanguageRepresentationFunctionType*)ctxt; + Ref<Function> funcObj = new Function(BNNewFunctionReference(func)); + Ref<DisassemblySettings> settingsObj = settings ? new DisassemblySettings(BNNewDisassemblySettingsReference(settings)) : nullptr; + auto lines = type->GetFunctionTypeTokens(funcObj, settingsObj); + *count = lines.size(); + BNDisassemblyTextLine* buf = new BNDisassemblyTextLine[lines.size()]; + for (size_t i = 0; i < lines.size(); i++) + { + const DisassemblyTextLine& line = lines[i]; + buf[i].addr = line.addr; + buf[i].instrIndex = line.instrIndex; + buf[i].highlight = line.highlight; + buf[i].tokens = InstructionTextToken::CreateInstructionTextTokenList(line.tokens); + buf[i].count = line.tokens.size(); + buf[i].tags = Tag::CreateTagList(line.tags, &(buf[i].tagCount)); + } + + return buf; +} + + +void LanguageRepresentationFunctionType::FreeLinesCallback(void*, BNDisassemblyTextLine* lines, size_t) +{ + delete[] lines; +} + + +Ref<LanguageRepresentationFunctionType> LanguageRepresentationFunctionType::GetByName(const std::string& name) +{ + BNLanguageRepresentationFunctionType* type = BNGetLanguageRepresentationFunctionTypeByName(name.c_str()); + if (!type) + return nullptr; + return new CoreLanguageRepresentationFunctionType(type); +} + + +bool LanguageRepresentationFunctionType::IsValidByName(const std::string& name, BinaryView* view) +{ + Ref<LanguageRepresentationFunctionType> type = GetByName(name); + if (!type) + return false; + return type->IsValid(view); +} + + +vector<Ref<LanguageRepresentationFunctionType>> LanguageRepresentationFunctionType::GetTypes() +{ + size_t count = 0; + BNLanguageRepresentationFunctionType** types = BNGetLanguageRepresentationFunctionTypeList(&count); + + vector<Ref<LanguageRepresentationFunctionType>> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new CoreLanguageRepresentationFunctionType(types[i])); + + BNFreeLanguageRepresentationFunctionTypeList(types); + return result; +} + + +CoreLanguageRepresentationFunctionType::CoreLanguageRepresentationFunctionType(BNLanguageRepresentationFunctionType* type): + LanguageRepresentationFunctionType(type) +{ +} + + +Ref<LanguageRepresentationFunction> CoreLanguageRepresentationFunctionType::Create( + Architecture* arch, Function* owner, HighLevelILFunction* highLevelIL) +{ + BNLanguageRepresentationFunction* func = BNCreateLanguageRepresentationFunction( + m_object, arch->GetObject(), owner->GetObject(), highLevelIL->GetObject()); + if (!func) + return nullptr; + return new CoreLanguageRepresentationFunction(func); +} + + +bool CoreLanguageRepresentationFunctionType::IsValid(BinaryView* view) +{ + return BNIsLanguageRepresentationFunctionTypeValid(m_object, view->GetObject()); +} + + +Ref<TypePrinter> CoreLanguageRepresentationFunctionType::GetTypePrinter() +{ + BNTypePrinter* printer = BNGetLanguageRepresentationFunctionTypePrinter(m_object); + if (!printer) + return nullptr; + return new CoreTypePrinter(printer); +} + + +Ref<TypeParser> CoreLanguageRepresentationFunctionType::GetTypeParser() +{ + BNTypeParser* parser = BNGetLanguageRepresentationFunctionTypeParser(m_object); + if (!parser) + return nullptr; + return new CoreTypeParser(parser); +} + + +vector<DisassemblyTextLine> CoreLanguageRepresentationFunctionType::GetFunctionTypeTokens( + Function* func, DisassemblySettings* settings) +{ + size_t count = 0; + BNDisassemblyTextLine* lines = BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens(m_object, + func->GetObject(), settings ? settings->GetObject() : nullptr, &count); + + vector<DisassemblyTextLine> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + DisassemblyTextLine line; + line.addr = lines[i].addr; + line.instrIndex = lines[i].instrIndex; + line.highlight = lines[i].highlight; + line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(lines[i].tokens, lines[i].count); + line.tags = Tag::ConvertTagList(lines[i].tags, lines[i].tagCount); + result.push_back(line); + } + + BNFreeDisassemblyTextLines(lines, count); + return result; +} diff --git a/linearviewobject.cpp b/linearviewobject.cpp index 3bd27b5a..4f00adb0 100644 --- a/linearviewobject.cpp +++ b/linearviewobject.cpp @@ -273,10 +273,11 @@ Ref<LinearViewObject> LinearViewObject::CreateHighLevelILSSAForm(BinaryView* vie } -Ref<LinearViewObject> LinearViewObject::CreateLanguageRepresentation(BinaryView* view, DisassemblySettings* settings) +Ref<LinearViewObject> LinearViewObject::CreateLanguageRepresentation(BinaryView* view, DisassemblySettings* settings, + const string& language) { return new LinearViewObject( - BNCreateLinearViewLanguageRepresentation(view->GetObject(), settings ? settings->GetObject() : nullptr)); + BNCreateLinearViewLanguageRepresentation(view->GetObject(), settings ? settings->GetObject() : nullptr, language.c_str())); } @@ -363,8 +364,8 @@ Ref<LinearViewObject> LinearViewObject::CreateSingleFunctionHighLevelILSSAForm( Ref<LinearViewObject> LinearViewObject::CreateSingleFunctionLanguageRepresentation( - Function* func, DisassemblySettings* settings) + Function* func, DisassemblySettings* settings, const string& language) { return new LinearViewObject(BNCreateLinearViewSingleFunctionLanguageRepresentation( - func->GetObject(), settings ? settings->GetObject() : nullptr)); + func->GetObject(), settings ? settings->GetObject() : nullptr, language.c_str())); } diff --git a/python/__init__.py b/python/__init__.py index 84494296..b6a3de97 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -79,6 +79,7 @@ from .debuginfo import * from .externallibrary import * from .undo import * from .fileaccessor import * +from .languagerepresentation 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/binaryview.py b/python/binaryview.py index fe270c9a..6adb8b1e 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -8545,7 +8545,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" def find_next_text( self, start: int, text: str, settings: Optional[_function.DisassemblySettings] = None, flags: FindFlag = FindFlag.FindCaseSensitive, - graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph + graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph ) -> Optional[int]: """ ``find_next_text`` searches for string ``text`` occurring in the linear view output starting at the virtual @@ -8562,7 +8562,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" FindCaseSensitive Case-sensitive search FindCaseInsensitive Case-insensitive search ==================== ============================ - :param FunctionGraphType graph_type: the IL to search within + :param FunctionViewType graph_type: the IL to search within """ if not isinstance(text, str): raise TypeError("text parameter is not str type") @@ -8572,13 +8572,14 @@ to a the type "tagRECT" found in the typelibrary "winX64common" raise TypeError("settings parameter is not DisassemblySettings type") result = ctypes.c_ulonglong() + graph_type = _function.FunctionViewType(graph_type)._to_core_struct() if not core.BNFindNextText(self.handle, start, text, result, settings.handle, flags, graph_type): return None return result.value def find_next_constant( self, start: int, constant: int, settings: Optional[_function.DisassemblySettings] = None, - graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph + graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph ) -> Optional[int]: """ ``find_next_constant`` searches for integer constant ``constant`` occurring in the linear view output starting at the virtual @@ -8587,7 +8588,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" :param int start: virtual address to start searching from. :param int constant: constant to search for :param DisassemblySettings settings: disassembly settings - :param FunctionGraphType graph_type: the IL to search within + :param FunctionViewType graph_type: the IL to search within """ if not isinstance(constant, int): raise TypeError("constant parameter is not integral type") @@ -8597,6 +8598,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" raise TypeError("settings parameter is not DisassemblySettings type") result = ctypes.c_ulonglong() + graph_type = _function.FunctionViewType(graph_type)._to_core_struct() if not core.BNFindNextConstant(self.handle, start, constant, result, settings.handle, graph_type): return None return result.value @@ -8706,7 +8708,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" def find_all_text( self, start: int, end: int, text: str, settings: Optional[_function.DisassemblySettings] = None, - flags=FindFlag.FindCaseSensitive, graph_type=FunctionGraphType.NormalFunctionGraph, progress_func=None, + flags=FindFlag.FindCaseSensitive, graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph, progress_func=None, match_callback=None ) -> QueueGenerator: """ @@ -8727,7 +8729,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" FindCaseSensitive Case-sensitive search FindCaseInsensitive Case-insensitive search ==================== ============================ - :param FunctionGraphType graph_type: the IL to search within + :param FunctionViewType graph_type: the IL to search within :param callback progress_func: optional function to be called with the current progress \ and total count. This function should return a boolean value that decides whether the \ search should continue or stop @@ -8752,6 +8754,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" raise TypeError("settings parameter is not DisassemblySettings type") if not isinstance(flags, FindFlag): raise TypeError('flag parameter must have type FindFlag') + graph_type = _function.FunctionViewType(graph_type)._to_core_struct() if progress_func: progress_func_obj = ctypes.CFUNCTYPE( @@ -8799,7 +8802,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" def find_all_constant( self, start: int, end: int, constant: int, settings: Optional[_function.DisassemblySettings] = None, - graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph, progress_func: Optional[ProgressFuncType] = None, + graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph, progress_func: Optional[ProgressFuncType] = None, match_callback: Optional[LineMatchCallbackType] = None ) -> QueueGenerator: """ @@ -8817,7 +8820,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" :param int constant: constant to search for :param DisassemblySettings settings: DisassemblySettings object used to render the text \ to be searched - :param FunctionGraphType graph_type: the IL to search within + :param FunctionViewType graph_type: the IL to search within :param callback progress_func: optional function to be called with the current progress \ and total count. This function should return a boolean value that decides whether the \ search should continue or stop @@ -8839,6 +8842,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" settings.set_option(DisassemblyOption.WaitForIL, True) if not isinstance(settings, _function.DisassemblySettings): raise TypeError("settings parameter is not DisassemblySettings type") + graph_type = _function.FunctionViewType(graph_type)._to_core_struct() if progress_func: progress_func_obj = ctypes.CFUNCTYPE( @@ -9628,6 +9632,21 @@ to a the type "tagRECT" found in the typelibrary "winX64common" def memory_map(self): return MemoryMap(handle=self.handle) + def stringify_unicode_data( + self, arch: Optional['architecture.Architecture'], buffer: 'databuffer.DataBuffer', + allow_short_strings: bool = False + ) -> Tuple[Optional[str], Optional[StringType]]: + string = ctypes.c_char_p() + string_type = ctypes.c_int() + if arch is not None: + arch = arch.handle + if not core.BNStringifyUnicodeData( + self.handle, arch, buffer.handle, allow_short_strings, ctypes.byref(string), ctypes.byref(string_type)): + return None, None + result = string.value + core.BNFreeString(string) + return result, StringType(string_type.value) + class BinaryReader: """ ``class BinaryReader`` is a convenience class for reading binary data. diff --git a/python/databuffer.py b/python/databuffer.py index ce8a68e7..36cf4430 100644 --- a/python/databuffer.py +++ b/python/databuffer.py @@ -143,8 +143,8 @@ class DataBuffer: return False return bytes(self) == bytes(other) - def escape(self, null_terminates=False) -> str: - return core.BNDataBufferToEscapedString(self.handle, null_terminates) + def escape(self, null_terminates=False, escape_printable=False) -> str: + return core.BNDataBufferToEscapedString(self.handle, null_terminates, escape_printable) def unescape(self) -> 'DataBuffer': return DataBuffer(handle=core.BNDecodeEscapedString(str(self))) diff --git a/python/examples/pseudo_python.py b/python/examples/pseudo_python.py new file mode 100644 index 00000000..b68b4786 --- /dev/null +++ b/python/examples/pseudo_python.py @@ -0,0 +1,1100 @@ +# Copyright (c) 2024 Vector 35 Inc +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +from binaryninja import (Architecture, BraceRequirement, DisassemblySettings, DisassemblyTextLine, Function, + InstructionTextToken, InstructionTextTokenType, LanguageRepresentationFunction, + LanguageRepresentationFunctionType, HighLevelILInstruction, HighLevelILFunction, + HighLevelILTokenEmitter, HighLevelILOperation, OperatorPrecedence, ScopeType, + SymbolDisplayType, SymbolDisplayResult, SymbolType, BoolType, VoidType, PointerType, + NamedTypeReferenceType, StructureType, InstructionTextTokenContext, StructureMember, + BinaryView, BuiltinType) +from typing import Optional +import struct + + +class PseudoPythonFunction(LanguageRepresentationFunction): + comment_start_string = "# " + + def perform_init_token_emitter(self, emitter): + # Python never allows braces, even if the user has set the option to show them + emitter.brace_requirement = BraceRequirement.BracesNotAllowed + + def perform_begin_lines(self, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter): + # Ensure that the function body is indented relative to the function declaration + tokens.increase_indent() + + def perform_get_expr_text( + self, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter, settings: Optional[DisassemblySettings], + as_full_ast: bool = True, precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence, + statement: bool = False + ): + with tokens.expr(instr): + if instr.operation == HighLevelILOperation.HLIL_BLOCK: + need_separator = False + body = instr.body + # Emit the lines for each statement in the body + for (idx, i) in enumerate(body): + # Don't display trailing return statements that don't have values + if (as_full_ast and idx + 1 == len(body) and i.operation == HighLevelILOperation.HLIL_RET and + len(i.src) == 0 and instr.expr_index == self.hlil.root.expr_index): + continue + + # If the statement is one that contains additional blocks of code, insert a scope separator + # to visually separate the logic. + has_blocks = i.operation in [HighLevelILOperation.HLIL_IF, HighLevelILOperation.HLIL_WHILE, + HighLevelILOperation.HLIL_DO_WHILE, HighLevelILOperation.HLIL_FOR, + HighLevelILOperation.HLIL_SWITCH] + if need_separator or (idx != 0 and has_blocks): + tokens.scope_separator() + need_separator = has_blocks + + # Emit the lines for the statement itself + self.perform_get_expr_text(i, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.new_line() + elif instr.operation == HighLevelILOperation.HLIL_IF: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "if ")) + self.perform_get_expr_text(instr.condition, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":\n")) + if as_full_ast: + # Only display the if body when printing the full AST. When printing basic blocks in graph view, + # the body of the if and the else part are rendered as other nodes in the graph. + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(instr.true, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + + # Else statements need to be handled as chains, since "else if" in Python should be rendered + # as "elif" statements. + if_chain = instr.false + while if_chain is not None and if_chain.operation not in [HighLevelILOperation.HLIL_NOP, + HighLevelILOperation.HLIL_UNREACHABLE]: + if if_chain.operation == HighLevelILOperation.HLIL_IF: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "elif ")) + self.perform_get_expr_text(if_chain.condition, tokens, settings, as_full_ast) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(if_chain.true, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + if_chain = if_chain.false + else: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "else")) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(if_chain, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + break + elif instr.operation == HighLevelILOperation.HLIL_FOR: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "for ")) + + # If the for loop can be represented as a Python range function, show it that way + dest_var = None + if instr.init.operation == HighLevelILOperation.HLIL_VAR_INIT: + dest_var = instr.init.dest + elif instr.init.operation == HighLevelILOperation.HLIL_ASSIGN and instr.init.dest.operation == HighLevelILOperation.HLIL_VAR: + dest_var = instr.init.dest.var + if (dest_var is not None and + (instr.condition.operation in [HighLevelILOperation.HLIL_CMP_SLT, + HighLevelILOperation.HLIL_CMP_ULT]) and + instr.condition.left.operation == HighLevelILOperation.HLIL_VAR and + instr.condition.left.var == dest_var and + instr.update.operation == HighLevelILOperation.HLIL_ASSIGN and + instr.update.dest.operation == HighLevelILOperation.HLIL_VAR and + instr.update.dest.var == instr.condition.left.var and + instr.update.src.operation == HighLevelILOperation.HLIL_ADD and + instr.update.src.left.operation == HighLevelILOperation.HLIL_VAR and + instr.update.src.left.var == instr.condition.left.var): + step_by = (instr.update.src.right.operation != HighLevelILOperation.HLIL_CONST or + instr.update.src.right.constant != 1) + start_required = (instr.init.src.operation != HighLevelILOperation.HLIL_CONST or + instr.init.src.constant != 0 or step_by) + tokens.append(InstructionTextToken(InstructionTextTokenType.LocalVariableToken, dest_var.name, + context=InstructionTextTokenContext.LocalVariableTokenContext, + address=instr.expr_index, value=dest_var.identifier, + size=instr.size)) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, " in ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "range")) + tokens.append_open_paren() + if start_required: + self.perform_get_expr_text(instr.init.src, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.condition.right, tokens, settings) + if step_by: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.update.src.right, tokens, settings) + tokens.append_close_paren() + else: + # Not representable directly as a Python loop, emit it with a more HLIL-like syntax + if instr.init.operation != HighLevelILOperation.HLIL_NOP: + self.perform_get_expr_text(instr.init, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "; ")) + if instr.condition.operation != HighLevelILOperation.HLIL_NOP: + self.perform_get_expr_text(instr.condition, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "; ")) + if instr.update.operation != HighLevelILOperation.HLIL_NOP: + self.perform_get_expr_text(instr.update, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + elif instr.operation == HighLevelILOperation.HLIL_WHILE: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "while ")) + self.perform_get_expr_text(instr.condition, tokens, settings, as_full_ast) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + elif instr.operation == HighLevelILOperation.HLIL_DO_WHILE: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "do")) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "while ")) + self.perform_get_expr_text(instr.condition, tokens, settings, as_full_ast) + elif instr.operation == HighLevelILOperation.HLIL_SWITCH: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "match ")) + self.perform_get_expr_text(instr.condition, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":")) + tokens.begin_scope(ScopeType.SwitchScopeType) + + # Output each case + for case in instr.cases: + self.perform_get_expr_text(case, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.new_line() + + # Check for default case + if instr.default is not None and instr.default.operation not in [HighLevelILOperation.HLIL_NOP, + HighLevelILOperation.HLIL_UNREACHABLE]: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "default")) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.begin_scope(ScopeType.CaseScopeType) + self.perform_get_expr_text(instr.default, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.CaseScopeType) + tokens.end_scope(ScopeType.SwitchScopeType) + elif instr.operation == HighLevelILOperation.HLIL_CASE: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "case ")) + for (i, value) in enumerate(instr.values): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " | ")) + self.perform_get_expr_text(value, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.begin_scope(ScopeType.CaseScopeType) + self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.CaseScopeType) + elif instr.operation == HighLevelILOperation.HLIL_BREAK: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "break")) + elif instr.operation == HighLevelILOperation.HLIL_CONTINUE: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "continue")) + elif instr.operation == HighLevelILOperation.HLIL_CALL: + self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + tokens.append_open_paren() + for (i, param) in enumerate(instr.params): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(param, tokens, settings) + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_TAILCALL: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "return ")) + self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + tokens.append_open_paren() + for (i, param) in enumerate(instr.params): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(param, tokens, settings) + tokens.append_close_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, " # tailcall")) + elif instr.operation == HighLevelILOperation.HLIL_INTRINSIC: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, instr.intrinsic.name)) + tokens.append_open_paren() + for (i, param) in enumerate(instr.params): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(param, tokens, settings) + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_SYSCALL: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "syscall")) + tokens.append_open_paren() + for (i, param) in enumerate(instr.params): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + if i == 0 and param.operation == HighLevelILOperation.HLIL_CONST: + platform = self.function.platform + if platform is not None: + syscall_name = platform.get_system_call_name(param.constant) + if len(syscall_name) > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, syscall_name)) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "{")) + self.perform_get_expr_text(param, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "}")) + continue + self.perform_get_expr_text(param, tokens, settings) + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ZX: + self.perform_get_expr_text(instr.src, tokens, settings) + elif instr.operation == HighLevelILOperation.HLIL_SX: + self.perform_get_expr_text(instr.src, tokens, settings) + elif instr.operation == HighLevelILOperation.HLIL_IMPORT: + # Check for import address symbol at target address, and display that if there is one + sym = instr.function.source_function.view.get_symbol_at(instr.constant) + if sym is not None: + if sym.type == SymbolType.ImportedDataSymbol or sym.type == SymbolType.ImportAddressSymbol: + sym = sym.imported_function_from_import_address_symbol(instr.constant) + if sym is not None: + tokens.append( + InstructionTextToken(InstructionTextTokenType.IndirectImportToken, sym.short_name, + value=instr.constant, address=instr.address, + size=instr.size, operand=instr.source_operand)) + return + # Otherwise use a generic pointer token + tokens.append_pointer_text_token(instr, instr.constant, settings, + SymbolDisplayType.DereferenceNonDataSymbols, precedence) + elif instr.operation == HighLevelILOperation.HLIL_ARRAY_INDEX: + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + tokens.append_open_bracket() + self.perform_get_expr_text(instr.index, tokens, settings) + tokens.append_close_bracket() + elif instr.operation == HighLevelILOperation.HLIL_VAR_INIT: + # Check to see if the variable appears live + appears_dead = False + ssa = instr.ssa_form + if ssa is not None and ssa.operation == HighLevelILOperation.HLIL_VAR_INIT_SSA: + appears_dead = not self.hlil.is_ssa_var_live(ssa.dest) + + # If the variable does not appear live, show the assignment as zero confidence (grayed out) + with tokens.force_zero_confidence(appears_dead): + tokens.append_var_text_token(instr.dest, instr, instr.size) + if instr.dest.type is not None: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) + tokens.append(instr.dest.type.get_tokens()) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = ")) + + # For the right side of the assignment, only use zero confidence if the instruction does + # not have any side effects + with tokens.force_zero_confidence(appears_dead and not instr.src.has_side_effects): + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.AssignmentOperatorPrecedence) + elif instr.operation == HighLevelILOperation.HLIL_VAR_DECLARE: + tokens.append_var_text_token(instr.var, instr, instr.size) + if instr.var.type is not None: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) + tokens.append(instr.var.type.get_tokens()) + elif instr.operation == HighLevelILOperation.HLIL_FLOAT_CONST: + # The constant value in the instruction contains the raw bits of the floating point value. Convert + # this to a floating point value and display it. + if instr.size == 4: + value = struct.unpack("<f", struct.pack("<I", instr.constant))[0] + tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}")) + elif instr.size == 8: + value = struct.unpack("<d", struct.pack("<Q", instr.constant))[0] + tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}")) + else: + tokens.append_integer_text_token(instr, instr.constant, instr.size) + elif instr.operation == HighLevelILOperation.HLIL_CONST: + # Check for bool type. Display these as True or False. The default handling will use C style + # booleans instead of Python style. + if instr.size == 0 or isinstance(instr.expr_type, BoolType): + if instr.constant != 0: + tokens.append( + InstructionTextToken(InstructionTextTokenType.IntegerToken, "True", address=instr.address, + value=instr.constant)) + else: + tokens.append( + InstructionTextToken(InstructionTextTokenType.IntegerToken, "False", address=instr.address, + value=instr.constant)) + else: + tokens.append_constant_text_token(instr, instr.constant, instr.size, settings, precedence) + elif instr.operation == HighLevelILOperation.HLIL_CONST_PTR: + tokens.append_pointer_text_token(instr, instr.constant, settings, + SymbolDisplayType.AddressOfDataSymbols, precedence) + elif instr.operation == HighLevelILOperation.HLIL_CONST_DATA: + # Constant data should be rendered according to the type of builtin function being used. + data, builtin = instr.constant_data.data_and_builtin + if builtin in [BuiltinType.BuiltinStrcpy, BuiltinType.BuiltinStrncpy]: + data = data.escape(null_terminates=True) + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, f'"{data}"', + address=instr.address, value=instr.constant_data.value, + context=InstructionTextTokenContext.ConstStringDataTokenContext)) + elif builtin == BuiltinType.BuiltinMemset: + tokens.append_open_brace() + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, + f"{instr.constant_data.value:#x}", + address=instr.address, value=instr.constant_data.value, + context=InstructionTextTokenContext.ConstDataTokenContext)) + tokens.append_close_brace() + else: + string, string_type = self.function.view.stringify_unicode_data(self.function.arch, data) + if string is not None: + wide_string_prefix = "" + token_context = InstructionTextTokenContext.ConstDataTokenContext + if builtin == BuiltinType.BuiltinWcscpy: + wide_string_prefix = "L" + token_context = InstructionTextTokenContext.ConstStringDataTokenContext + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, + f'{wide_string_prefix}"{string}"', + address=instr.address, value=instr.constant_data.value, + context=token_context)) + else: + data = data.escape(null_terminates=False, escape_printable=True) + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, f'"{data}"', + address=instr.address, value=instr.constant_data.value, + context=InstructionTextTokenContext.ConstDataTokenContext)) + elif instr.operation == HighLevelILOperation.HLIL_EXTERN_PTR: + # Extern pointer instructions have an offset associated with them. If this is nonzero, show this + # as an addition or subtraction of the offset. + parens = instr.offset != 0 and precedence >= OperatorPrecedence.AddOperatorPrecedence + if parens: + tokens.append_open_paren() + if instr.offset != 0: + precedence = OperatorPrecedence.SubOperatorPrecedence + tokens.append_pointer_text_token(instr, instr.constant, settings, + SymbolDisplayType.AddressOfDataSymbols, precedence) + if instr.offset < 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " - ")) + tokens.append_integer_text_token(instr, -instr.offset, instr.size) + elif instr.offset > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " + ")) + tokens.append_integer_text_token(instr, instr.offset, instr.size) + + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_VAR: + tokens.append(InstructionTextToken(InstructionTextTokenType.LocalVariableToken, instr.var.name, + address=instr.expr_index, size=instr.size, + value=instr.var.identifier, + context=InstructionTextTokenContext.LocalVariableTokenContext)) + elif instr.operation == HighLevelILOperation.HLIL_ASSIGN: + # Check to see if the variable appears live + appears_dead = False + if instr.dest.operation == HighLevelILOperation.HLIL_VAR: + ssa = instr.ssa_form + if ssa is not None and ssa.operation == HighLevelILOperation.HLIL_VAR_INIT_SSA: + appears_dead = not self.hlil.is_ssa_var_live(ssa.dest) + + # If the variable does not appear live, show the assignment as zero confidence (grayed out) + with tokens.force_zero_confidence(appears_dead): + self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast, + OperatorPrecedence.AssignmentOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = ")) + + # For the right side of the assignment, only use zero confidence if the instruction does + # not have any side effects + with tokens.force_zero_confidence(appears_dead and not instr.src.has_side_effects): + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.AssignmentOperatorPrecedence) + elif instr.operation == HighLevelILOperation.HLIL_ASSIGN_UNPACK: + tokens.append_open_paren() + for (i, dest) in enumerate(instr.dest): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(dest, tokens, settings) + tokens.append_close_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = ")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.AssignmentOperatorPrecedence) + elif instr.operation == HighLevelILOperation.HLIL_STRUCT_FIELD: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + self.append_field_text_tokens(instr.src, instr.offset, instr.member_index, instr.size, tokens) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_DEREF_FIELD: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + if instr.src.operation == HighLevelILOperation.HLIL_CONST_PTR: + tokens.append_pointer_text_token(instr.src, instr.src.constant, settings, + SymbolDisplayType.DisplaySymbolOnly, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + else: + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + self.append_field_text_tokens(instr.src, instr.offset, instr.member_index, instr.size, tokens) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_DEREF: + if instr.src.operation == HighLevelILOperation.HLIL_CONST_PTR: + if tokens.append_pointer_text_token(instr.src, instr.src.constant, settings, + SymbolDisplayType.DereferenceNonDataSymbols, + precedence) == SymbolDisplayResult.DataSymbolResult: + expr_type = instr.src.expr_type + if isinstance(expr_type, PointerType) and expr_type.target.width != instr.size: + # If dereference size doesn't match the data variable size, append a size suffix + suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"} + if instr.size in suffix: + suffix_str = suffix[instr.size] + else: + suffix_str = f".{instr.size}" + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, suffix_str)) + else: + parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "*")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.UnaryOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ADDRESS_OF: + parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "&")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.UnaryOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_E, HighLevelILOperation.HLIL_FCMP_E]: + parens = precedence > OperatorPrecedence.EqualityOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.EqualityOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " == ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.EqualityOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_NE, HighLevelILOperation.HLIL_FCMP_NE]: + parens = precedence > OperatorPrecedence.EqualityOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.EqualityOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " != ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.EqualityOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_SLT, HighLevelILOperation.HLIL_CMP_ULT, + HighLevelILOperation.HLIL_FCMP_LT]: + parens = precedence > OperatorPrecedence.CompareOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " < ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_SLE, HighLevelILOperation.HLIL_CMP_ULE, + HighLevelILOperation.HLIL_FCMP_LE]: + parens = precedence > OperatorPrecedence.CompareOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " <= ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_SGE, HighLevelILOperation.HLIL_CMP_UGE, + HighLevelILOperation.HLIL_FCMP_GE]: + parens = precedence > OperatorPrecedence.CompareOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " >= ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_SGT, HighLevelILOperation.HLIL_CMP_UGT, + HighLevelILOperation.HLIL_FCMP_GT]: + parens = precedence > OperatorPrecedence.CompareOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " > ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_AND: + if instr.size == 0: + # Size of zero is a boolean operation, show the boolean operator name + parens = (precedence >= OperatorPrecedence.BitwiseOrOperatorPrecedence or + precedence == OperatorPrecedence.LogicalOrOperatorPrecedence) + operation = "and" + precedence = OperatorPrecedence.LogicalAndOperatorPrecedence + else: + parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or + precedence in [OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + operation = "&" + precedence = OperatorPrecedence.BitwiseAndOperatorPrecedence + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens(operation, instr, tokens, settings, as_full_ast, precedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_OR: + if instr.size == 0: + # Size of zero is a boolean operation, show the boolean operator name + parens = (precedence >= OperatorPrecedence.BitwiseOrOperatorPrecedence or + precedence == OperatorPrecedence.LogicalAndOperatorPrecedence) + operation = "or" + precedence = OperatorPrecedence.LogicalOrOperatorPrecedence + else: + parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or + precedence in [OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + operation = "|" + precedence = OperatorPrecedence.BitwiseOrOperatorPrecedence + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens(operation, instr, tokens, settings, as_full_ast, precedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_XOR: + parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or + precedence in [OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("^", instr, tokens, settings, as_full_ast, + OperatorPrecedence.BitwiseXorOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ADC: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "adc")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.carry, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_SBB: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "sbb")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.carry, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ADD_OVERFLOW: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "add_overflow")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_ADD, HighLevelILOperation.HLIL_FADD]: + parens = (precedence > OperatorPrecedence.AddOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("+", instr, tokens, settings, as_full_ast, + OperatorPrecedence.AddOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_SUB, HighLevelILOperation.HLIL_FSUB]: + parens = (precedence > OperatorPrecedence.AddOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("-", instr, tokens, settings, as_full_ast, + OperatorPrecedence.SubOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_MUL, HighLevelILOperation.HLIL_MULS_DP, + HighLevelILOperation.HLIL_MULU_DP, HighLevelILOperation.HLIL_FMUL]: + parens = (precedence > OperatorPrecedence.MultiplyOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("*", instr, tokens, settings, as_full_ast, + OperatorPrecedence.MultiplyOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_DIVS, HighLevelILOperation.HLIL_DIVU, + HighLevelILOperation.HLIL_DIVS_DP, HighLevelILOperation.HLIL_DIVU_DP]: + parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("//", instr, tokens, settings, as_full_ast, + OperatorPrecedence.DivideOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_MODS, HighLevelILOperation.HLIL_MODU, + HighLevelILOperation.HLIL_MODS_DP, HighLevelILOperation.HLIL_MODU_DP]: + parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("%", instr, tokens, settings, as_full_ast, + OperatorPrecedence.DivideOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FDIV: + parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("/", instr, tokens, settings, as_full_ast, + OperatorPrecedence.DivideOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_LSL: + parens = precedence > OperatorPrecedence.ShiftOperatorPrecedence + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("<<", instr, tokens, settings, as_full_ast, + OperatorPrecedence.ShiftOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_LSR, HighLevelILOperation.HLIL_ASR]: + parens = precedence > OperatorPrecedence.ShiftOperatorPrecedence + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens(">>", instr, tokens, settings, as_full_ast, + OperatorPrecedence.ShiftOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ROL: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rol")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ROR: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "ror")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_RLC: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rlc")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.carry, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_RRC: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rrc")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.carry, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_TEST_BIT: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "test_bit")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FLOOR: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "floor")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_CEIL: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "ceil")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FTRUNC: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "trunc")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FABS: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fabs")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FSQRT: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "sqrt")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ROUND_TO_INT: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "round")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FCMP_O: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fcmp_o")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FCMP_UO: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fcmp_uo")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_NOT: + parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence + if parens: + tokens.append_open_paren() + if instr.size == 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "not ")) + else: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "~")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.UnaryOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_NEG, HighLevelILOperation.HLIL_FNEG]: + parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "-")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.UnaryOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_FLOAT_CONV, HighLevelILOperation.HLIL_INT_TO_FLOAT]: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "float")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_FLOAT_TO_INT, HighLevelILOperation.HLIL_BOOL_TO_INT]: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "int")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_RET: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "return")) + operands = instr.src + if len(operands) > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " ")) + if len(operands) > 1: + tokens.append_open_paren() + for (i, operand) in enumerate(operands): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(operand, tokens, settings) + if len(operands) > 1: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_NORET: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# no return")) + elif instr.operation == HighLevelILOperation.HLIL_UNREACHABLE: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# unreachable")) + elif instr.operation == HighLevelILOperation.HLIL_UNDEF: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# undefined")) + elif instr.operation == HighLevelILOperation.HLIL_NOP: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# nop")) + elif instr.operation == HighLevelILOperation.HLIL_BP: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "breakpoint")) + tokens.append_open_paren() + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_JUMP: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# jump -> ")) + self.perform_get_expr_text(instr.dest, tokens, settings) + elif instr.operation == HighLevelILOperation.HLIL_TRAP: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "trap")) + tokens.append_open_paren() + tokens.append_integer_text_token(instr, instr.vector, 8) + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_GOTO: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "goto ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.GotoLabelToken, instr.target.name, + instr.target.label_id)) + elif instr.operation == HighLevelILOperation.HLIL_LABEL: + tokens.decrease_indent() + tokens.append(InstructionTextToken(InstructionTextTokenType.GotoLabelToken, instr.target.name, + instr.target.label_id)) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.increase_indent() + elif instr.operation == HighLevelILOperation.HLIL_LOW_PART: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"} + if instr.size in suffix: + suffix_str = suffix[instr.size] + else: + suffix_str = f".{instr.size}" + tokens.append(InstructionTextToken(InstructionTextTokenType.StructOffsetToken, suffix_str, value=0, + size=instr.size)) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_SPLIT: + tokens.append_open_paren() + self.perform_get_expr_text(instr.high, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.low, tokens, settings) + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_UNIMPL, HighLevelILOperation.HLIL_UNIMPL_MEM]: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# ")) + for token in instr.tokens: + token.token_type = InstructionTextTokenType.AnnotationToken + tokens.append(token) + else: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, + f"# unimplemented {instr.operation.name}")) + + def append_two_operand_tokens(self, operation: str, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter, + settings: Optional[DisassemblySettings], as_full_ast: bool, + precedence: OperatorPrecedence): + if precedence == OperatorPrecedence.SubOperatorPrecedence: + # Treat left side of subtraction as same level as addition. This lets + # (a - b) - c be represented as a - b - c, but a - (b - c) does not + # simplify at rendering + left_precedence = OperatorPrecedence.AddOperatorPrecedence + elif precedence == OperatorPrecedence.DivideOperatorPrecedence: + # Treat left side of divison as same level as multiplication. This lets + # (a / b) / c be represented as a / b / c, but a / (b / c) does not + # simplify at rendering + left_precedence = OperatorPrecedence.MultiplyOperatorPrecedence + else: + left_precedence = precedence + + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, left_precedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, f" {operation} ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, precedence) + + def append_field_text_tokens(self, var: HighLevelILInstruction, offset: int, member_index: int, size: int, + tokens: HighLevelILTokenEmitter): + var_type = var.expr_type + # Follow pointer type to its target + if isinstance(var_type, PointerType): + var_type = var_type.target + # Follow named type references to the target + if isinstance(var_type, NamedTypeReferenceType): + target_type = var_type.target(var.function.view) + if target_type is not None: + var_type = target_type + + has_field = False + if isinstance(var_type, StructureType): + # For structures, resolve field names using the type API + class Resolver: + def __init__(self, view: BinaryView, offset: int): + self.has_field = False + self.correct_size = False + self.offset = offset + self.view = view + + def resolve_func(self, base_name: Optional[NamedTypeReferenceType], + resolved_struct: Optional[StructureType], resolved_member_index: int, + struct_offset: int, adjusted_offset: int, member: StructureMember): + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, ".")) + name_list = HighLevelILTokenEmitter.names_for_outer_structure_members( + self.view, var_type, var) + [member.name] + tokens.append(InstructionTextToken(InstructionTextTokenType.FieldNameToken, member.name, + value=struct_offset + member.offset, typeNames=name_list)) + self.offset = adjusted_offset - member.offset + self.has_field = True + self.correct_size = member.type is not None and size == member.type.width + + resolver = Resolver(self.function.view, offset) + result = var_type.resolve_member_or_base_member(resolver.view, offset, 0, resolver.resolve_func) + if result and resolver.has_field and resolver.correct_size: + # If the field was matched, we're done + return + has_field = resolver.has_field + offset = resolver.offset + + # Generate offset syntax for the missing field + suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"} + if size in suffix: + suffix_str = suffix[size] + else: + suffix_str = f".{size}" + if (has_field or not isinstance(var_type, StructureType)) and offset == 0: + # No offset, just display a size suffix + offset_str = suffix_str + else: + # Has an offset + offset_str = f".__offset({offset:#x}){suffix_str}" + + name_list = HighLevelILTokenEmitter.names_for_outer_structure_members( + self.function.view, var_type, var) + [offset_str] + tokens.append(InstructionTextToken(InstructionTextTokenType.StructOffsetToken, offset_str, value=offset, + size=size, typeNames=name_list)) + + +class PseudoPythonFunctionType(LanguageRepresentationFunctionType): + language_name = "Pseudo Python" + + def create(self, arch: Architecture, owner: Function, hlil: HighLevelILFunction): + return PseudoPythonFunction(arch, owner, hlil) + + def function_type_tokens(self, func: Function, settings: DisassemblySettings) -> DisassemblyTextLine: + tokens = [] + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "def ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.CodeSymbolToken, func.name, value=func.start)) + tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "(")) + for (i, param) in enumerate(func.type.parameters_with_all_locations): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.ArgumentNameToken, param.name, + context=InstructionTextTokenContext.LocalVariableTokenContext, + address=param.location.identifier)) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) + for token in param.type.get_tokens(): + token.context = InstructionTextTokenContext.LocalVariableTokenContext + token.address = param.location.identifier + tokens.append(token) + tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, ")")) + if func.can_return.value and func.type.return_value is not None and not isinstance(func.type.return_value, VoidType): + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " -> ")) + for token in func.type.return_value.get_tokens(): + token.context = InstructionTextTokenContext.FunctionReturnTokenContext + tokens.append(token) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + return [DisassemblyTextLine(tokens, func.start)] + + +PseudoPythonFunctionType().register() diff --git a/python/function.py b/python/function.py index 5ed61603..c8acf287 100644 --- a/python/function.py +++ b/python/function.py @@ -27,8 +27,9 @@ from dataclasses import dataclass # Binary Ninja components from . import _binaryninjacore as core from .enums import ( - AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, HighlightStandardColor, - HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType + AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, HighlightStandardColor, + HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType, + BuiltinType ) from .exceptions import ILException @@ -45,6 +46,7 @@ from . import variable from . import flowgraph from . import callingconvention from . import workflow +from . import languagerepresentation from . import deprecation from . import __version__ @@ -76,6 +78,7 @@ ILFunctionType = Union['lowlevelil.LowLevelILFunction', 'mediumlevelil.MediumLev ILInstructionType = Union['lowlevelil.LowLevelILInstruction', 'mediumlevelil.MediumLevelILInstruction', 'highlevelil.HighLevelILInstruction'] StringOrType = Union[str, 'types.Type', 'types.TypeBuilder'] +FunctionViewTypeOrName = Union['FunctionViewType', FunctionGraphType, str] def _function_name_(): @@ -181,6 +184,39 @@ class VariableReferenceSource: return f"<var: {repr(self.var)}, src: {repr(self.src)}>" +@dataclass +class FunctionViewType: + view_type: FunctionGraphType + name: Optional[str] + + def __init__(self, view_type: FunctionViewTypeOrName): + if isinstance(view_type, FunctionViewType): + self.view_type = view_type.view_type + self.name = view_type.name + if isinstance(view_type, FunctionGraphType): + self.view_type = view_type + self.name = None + else: + self.view_type = FunctionGraphType.HighLevelLanguageRepresentationFunctionGraph + self.name = str(view_type) + + @staticmethod + def _from_core_struct(view_type: core.BNFunctionViewType) -> 'FunctionViewType': + if view_type.type == FunctionGraphType.HighLevelLanguageRepresentationFunctionGraph: + if view_type.name is None: + return FunctionViewType("Pseudo C") + else: + return FunctionViewType(view_type.name) + else: + return FunctionViewType(view_type.type) + + def _to_core_struct(self) -> core.BNFunctionViewType: + result = core.BNFunctionViewType() + result.type = self.view_type + result.name = self.name + return result + + class BasicBlockList: def __init__( self, function: Union['Function', 'lowlevelil.LowLevelILFunction', 'mediumlevelil.MediumLevelILFunction', @@ -1069,6 +1105,30 @@ class Function: return highlevelil.HighLevelILFunction(self.arch, result, self) @property + def pseudo_c(self) -> Optional['languagerepresentation.LanguageRepresentationFunction']: + return self.language_representation("Pseudo C") + + @property + def pseudo_c_if_available(self) -> Optional['languagerepresentation.LanguageRepresentationFunction']: + return self.language_representation_if_available("Pseudo C") + + def language_representation( + self, language: str + ) -> Optional['languagerepresentation.LanguageRepresentationFunction']: + result = core.BNGetFunctionLanguageRepresentation(self.handle, language) + if result is None: + return None + return languagerepresentation.LanguageRepresentationFunction(handle=result) + + def language_representation_if_available( + self, language: str + ) -> Optional['languagerepresentation.LanguageRepresentationFunction']: + result = core.BNGetFunctionLanguageRepresentationIfAvailable(self.handle, language) + if result is None: + return None + return languagerepresentation.LanguageRepresentationFunction(handle=result) + + @property def type(self) -> 'types.FunctionType': """ Function type object, can be set with either a string representing the function prototype @@ -1787,7 +1847,15 @@ class Function: core.BNFreeILInstructionList(exits) def get_constant_data(self, state: RegisterValueType, value: int, size: int = 0) -> databuffer.DataBuffer: - return databuffer.DataBuffer(handle=core.BNGetConstantData(self.handle, state, value, size)) + return databuffer.DataBuffer(handle=core.BNGetConstantData(self.handle, state, value, size, None)) + + def get_constant_data_and_builtin( + self, state: RegisterValueType, value: int, size: int = 0 + ) -> Tuple[databuffer.DataBuffer, BuiltinType]: + builtin = ctypes.c_int() + db = databuffer.DataBuffer( + handle=core.BNGetConstantData(self.handle, state, value, size, ctypes.byref(builtin))) + return db, BuiltinType(builtin.value) def get_reg_value_at( self, addr: int, reg: 'architecture.RegisterType', arch: Optional['architecture.Architecture'] = None @@ -2078,13 +2146,14 @@ class Function: return result def create_graph( - self, graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph, + self, graph_type: FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph, settings: Optional['DisassemblySettings'] = None ) -> flowgraph.CoreFlowGraph: if settings is not None: settings_obj = settings.handle else: settings_obj = None + graph_type = FunctionViewType(graph_type)._to_core_struct() return flowgraph.CoreFlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj)) def apply_imported_types(self, sym: 'types.CoreSymbol', type: Optional[StringOrType] = None) -> None: diff --git a/python/highlevelil.py b/python/highlevelil.py index 48cbd6cc..21359685 100644 --- a/python/highlevelil.py +++ b/python/highlevelil.py @@ -907,6 +907,10 @@ class HighLevelILInstruction(BaseILInstruction): return False return True + @property + def has_side_effects(self) -> bool: + return core.BNHighLevelILHasSideEffects(self.function.handle, self.expr_index) + @dataclass(frozen=True, repr=False, eq=False) class HighLevelILUnaryBase(HighLevelILInstruction, UnaryOperation): diff --git a/python/languagerepresentation.py b/python/languagerepresentation.py new file mode 100644 index 00000000..49a300c1 --- /dev/null +++ b/python/languagerepresentation.py @@ -0,0 +1,822 @@ +# Copyright (c) 2024 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 ctypes +import traceback +from typing import List, Optional, Union + +# Binary Ninja components +import binaryninja +from . import _binaryninjacore as core +from . import architecture +from . import binaryview +from . import function +from . import highlevelil +from . import highlight +from . import variable +from . import types +from .log import log_error +from .enums import BraceRequirement, HighlightStandardColor, InstructionTextTokenType, OperatorPrecedence, ScopeType, \ + SymbolDisplayType, SymbolDisplayResult + + +class HighLevelILTokenEmitter: + """ + ``class HighLevelILTokenEmitter`` contains methods for emitting text tokens for High Level IL instructions. + Methods are provided for typical patterns found in various high level languages. + + This class cannot be instantiated directly. An instance of the class will be provided when the methods + in ``class LanguageRepresentationFunction`` are called. + """ + def __init__(self, handle: core.BNHighLevelILTokenEmitterHandle): + self.handle = handle + + def __del__(self): + if core is not None: + core.BNFreeHighLevelILTokenEmitter(self.handle) + + def new_line(self): + """Starts a new line in the output.""" + core.BNHighLevelILTokenEmitterNewLine(self.handle) + + def increase_indent(self): + """Increases the indentation level by one.""" + core.BNHighLevelILTokenEmitterIncreaseIndent(self.handle) + + def decrease_indent(self): + """Decreases the indentation level by one.""" + core.BNHighLevelILTokenEmitterDecreaseIndent(self.handle) + + def scope_separator(self): + """ + Indicates that visual separation of scopes is desirable at the current position. By default, + this will insert a blank line, but this can be configured by the user. + """ + core.BNHighLevelILTokenEmitterScopeSeparator(self.handle) + + def begin_scope(self, scope_type: ScopeType): + """Begins a new scope. Insertion of newlines and braces will be handled using the current settings.""" + core.BNHighLevelILTokenEmitterBeginScope(self.handle, scope_type) + + def end_scope(self, scope_type: ScopeType): + """Ends the current scope.""" + core.BNHighLevelILTokenEmitterEndScope(self.handle, scope_type) + + def scope_continuation(self, force_same_line: bool): + """Continues the previous scope with a new associated scope. This is most commonly used for ``else`` statements.""" + core.BNHighLevelILTokenEmitterScopeContinuation(self.handle, force_same_line) + + def finalize_scope(self): + """Finalizes the previous scope, indicating that there are no more associated scopes.""" + core.BNHighLevelILTokenEmitterFinalizeScope(self.handle) + + def no_indent_for_this_line(self): + """Forces there to be no indentation for the next line.""" + core.BNHighLevelILTokenEmitterNoIndentForThisLine(self.handle) + + class ZeroConfidenceContext: + """ + ``class ZeroConfidenceContext`` is a context manager that optionally forces tokens to be of zero confidence + inside the context. + """ + def __init__(self, emitter: 'HighLevelILTokenEmitter', enabled: bool): + self.emitter = emitter + self.enabled = enabled + + def __enter__(self): + if self.enabled: + core.BNHighLevelILTokenEmitterBeginForceZeroConfidence(self.emitter.handle) + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.enabled: + core.BNHighLevelILTokenEmitterEndForceZeroConfidence(self.emitter.handle) + + def force_zero_confidence(self, enabled: bool = True) -> 'HighLevelILTokenEmitter.ZeroConfidenceContext': + """ + Returns a context manager that forces tokens inside of it to be of zero confidence. If ``False`` is passed + to this method, the context has no effect, allowing the caller to conditionally apply this behavior. + """ + return HighLevelILTokenEmitter.ZeroConfidenceContext(self, enabled) + + class ExprContext: + """ + ``class ExprContext`` is a context manager that associates the tokens inside the context with the given + High Level IL expression. + """ + def __init__(self, emitter: 'HighLevelILTokenEmitter', hlil_expr: highlevelil.HighLevelILInstruction): + self.emitter = emitter + self.expr = hlil_expr + self.prev_expr = None + + def __enter__(self): + expr = core.BNTokenEmitterExpr() + expr.address = self.expr.address + expr.sourceOperand = self.expr.source_operand + expr.exprIndex = self.expr.expr_index + expr.instrIndex = self.expr.instr_index + self.prev_expr = core.BNHighLevelILTokenEmitterSetCurrentExpr(self.emitter.handle, expr) + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.prev_expr is not None: + core.BNHighLevelILTokenEmitterRestoreCurrentExpr(self.emitter.handle, self.prev_expr) + + def expr(self, hlil_expr: 'highlevelil.HighLevelILInstruction') -> 'HighLevelILTokenEmitter.ExprContext': + """ + Returns a context manager that associates the tokens inside the context with the given High Level IL expression. + """ + return HighLevelILTokenEmitter.ExprContext(self, hlil_expr) + + def finalize(self): + """Finalizes all tokens in the output.""" + core.BNHighLevelILTokenEmitterFinalize(self.handle) + + def append(self, tokens: Union['architecture.InstructionTextToken', List['architecture.InstructionTextToken']]): + """Appends a token or list of tokens to the output.""" + if not isinstance(tokens, list): + tokens = [tokens] + buf = architecture.InstructionTextToken._get_core_struct(tokens) + for i in range(len(tokens)): + core.BNHighLevelILTokenEmitterAppend(self.handle, buf[i]) + + def append_open_paren(self): + """Appends an open parenthesis (``(``) to the output.""" + core.BNHighLevelILTokenEmitterAppendOpenParen(self.handle) + + def append_close_paren(self): + """Appends a close parenthesis (``)``) to the output.""" + core.BNHighLevelILTokenEmitterAppendCloseParen(self.handle) + + def append_open_bracket(self): + """Appends an open bracket (``[``) to the output.""" + core.BNHighLevelILTokenEmitterAppendOpenBracket(self.handle) + + def append_close_bracket(self): + """Appends a close bracket (``]``) to the output.""" + core.BNHighLevelILTokenEmitterAppendCloseBracket(self.handle) + + def append_open_brace(self): + """Appends an open brace (``{``) to the output.""" + core.BNHighLevelILTokenEmitterAppendOpenBrace(self.handle) + + def append_close_brace(self): + """Appends a close brace (``}``) to the output.""" + core.BNHighLevelILTokenEmitterAppendCloseBrace(self.handle) + + def append_semicolon(self): + """Appends a semicolon (``;``) to the output.""" + core.BNHighLevelILTokenEmitterAppendSemicolon(self.handle) + + @property + def current_tokens(self) -> List['function.InstructionTextToken']: + """The list of tokens on the current line (read-only).""" + count = ctypes.c_ulonglong() + tokens = core.BNHighLevelILTokenEmitterGetCurrentTokens(self.handle, count) + result = [] + if tokens is not None: + result = function.InstructionTextToken._from_core_struct(tokens, count.value) + core.BNFreeInstructionText(tokens, count.value) + return result + + @property + def lines(self) -> List['function.DisassemblyTextLine']: + """The list of lines in the output (read-only).""" + count = ctypes.c_ulonglong() + lines = core.BNHighLevelILTokenEmitterGetLines(self.handle, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, color=color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + @property + def brace_requirement(self) -> BraceRequirement: + """The requirement for insertion of braces around scopes in the output.""" + return core.BNHighLevelILTokenEmitterGetBraceRequirement(self.handle) + + @brace_requirement.setter + def brace_requirement(self, value: BraceRequirement): + core.BNHighLevelILTokenEmitterSetBraceRequirement(self.handle, value) + + @property + def braces_around_switch_cases(self): + """Whether cases within switch statements should always have braces around them.""" + return core.BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(self.handle) + + @braces_around_switch_cases.setter + def braces_around_switch_cases(self, value: bool): + core.BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(self.handle, value) + + @property + def default_braces_on_same_line(self): + """ + Whether braces should default to being on the same line as the statement that begins the scope. + If the user has explicitly set a preference, this setting will be ignored and the user's preference + will be used instead. + """ + return core.BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(self.handle) + + @default_braces_on_same_line.setter + def default_braces_on_same_line(self, value: bool): + core.BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(self.handle, value) + + @property + def simple_scope_allowed(self): + """Whether omitting braces around single-line scopes is allowed.""" + return core.BNHighLevelILTokenEmitterIsSimpleScopeAllowed(self.handle) + + @simple_scope_allowed.setter + def simple_scope_allowed(self, value: bool): + core.BNHighLevelILTokenEmitterSetSimpleScopeAllowed(self.handle, value) + + def append_size_token(self, size: int, token_type: InstructionTextTokenType): + """Appends a size token for the given size in the High Level IL syntax.""" + core.BNAddHighLevelILSizeToken(size, token_type, self.handle) + + def append_float_size_token(self, size: int, token_type: InstructionTextTokenType): + """Appends a floating point size token for the given size in the High Level IL syntax.""" + core.BNAddHighLevelILFloatSizeToken(size, token_type, self.handle) + + def append_var_text_token( + self, var: 'variable.CoreVariable', instr: 'highlevelil.HighLevelILInstruction', + size: int + ): + """Appends tokens for access to a variable.""" + core.BNAddHighLevelILVarTextToken( + instr.function.handle, var.to_BNVariable(), self.handle, instr.expr_index, size) + + def append_integer_text_token(self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int): + """Appends tokens for a constant intenger value.""" + core.BNAddHighLevelILIntegerTextToken(instr.function.handle, instr.expr_index, value, size, self.handle) + + def append_array_index_token( + self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int, address: int = 0): + """Appends tokens for accessing an array by index.""" + core.BNAddHighLevelILArrayIndexToken(instr.function.handle, instr.expr_index, value, size, self.handle, address) + + def append_pointer_text_token( + self, instr: 'highlevelil.HighLevelILInstruction', value: int, + settings: Optional['function.DisassemblySettings'], symbol_display: SymbolDisplayType, + precedence: OperatorPrecedence, allow_short_string: bool = False + ) -> SymbolDisplayResult: + """Appends tokens for displaying a constant pointer value.""" + if settings is not None: + settings = settings.handle + return SymbolDisplayResult(core.BNAddHighLevelILPointerTextToken( + instr.function.handle, instr.expr_index, value, self.handle, settings, symbol_display, precedence, + allow_short_string)) + + def append_constant_text_token( + self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int, + settings: Optional['function.DisassemblySettings'], precedence: OperatorPrecedence + ): + """Appends tokens for a constant value.""" + if settings is not None: + settings = settings.handle + core.BNAddHighLevelILConstantTextToken( + instr.function.handle, instr.expr_index, value, size, self.handle, settings, precedence) + + @staticmethod + def names_for_outer_structure_members( + view: 'binaryview.BinaryView', struct_type: 'types.Type', var: 'highlevelil.HighLevelILInstruction', + ) -> List[str]: + """ + Gets the list of names for the outer structure members when accessing a structure member. This list + can be passed for the ``typeNames`` parameter of the ``class InstructionTextToken`` constructor. + """ + count = ctypes.c_ulonglong() + result = core.BNAddNamesForOuterStructureMembers( + view.handle, struct_type.handle, var.function.handle, var.expr_index, count) + names = [] + if result is not None: + for i in range(count.value): + names.append(result[i].decode("utf-8")) + core.BNFreeStringList(result, count.value) + return names + + +class LanguageRepresentationFunction: + """ + ``class LanguageRepresentationFunction`` represents a single function in a registered high level language. + """ + _registered_instances = [] + comment_start_string = "// " + comment_end_string = "" + annotation_start_string = "{" + annotation_end_string = "}" + + def __init__( + self, arch: Optional['architecture.Architecture'] = None, owner: Optional['function.Function'] = None, + hlil: Optional['highlevelil.HighLevelILFunction'] = None, handle=None + ): + if handle is None: + if arch is None: + raise ValueError("function representation must have an associated architecture") + if owner is None: + raise ValueError("function representation must have an owning function") + if hlil is None: + raise ValueError("function representation must have an associated High Level IL function") + self._cb = core.BNCustomLanguageRepresentationFunction() + self._cb.context = 0 + self._cb.externalRefTaken = self._cb.externalRefTaken.__class__(self._external_ref_taken) + self._cb.externalRefReleased = self._cb.externalRefReleased.__class__(self._external_ref_released) + self._cb.initTokenEmitter = self._cb.initTokenEmitter.__class__(self._init_token_emitter) + self._cb.getExprText = self._cb.getExprText.__class__(self._get_expr_text) + self._cb.beginLines = self._cb.beginLines.__class__(self._begin_lines) + self._cb.endLines = self._cb.endLines.__class__(self._end_lines) + self._cb.getCommentStartString = self._cb.getCommentStartString.__class__(self._comment_start_string) + self._cb.getCommentEndString = self._cb.getCommentEndString.__class__(self._comment_end_string) + self._cb.getAnnotationStartString = self._cb.getAnnotationStartString.__class__( + self._annotation_start_string) + self._cb.getAnnotationEndString = self._cb.getAnnotationEndString.__class__(self._annotation_end_string) + self.comment_start_string = self.__class__.comment_start_string + self.comment_end_string = self.__class__.comment_end_string + self.annotation_start_string = self.__class__.annotation_start_string + self.annotation_end_string = self.__class__.annotation_end_string + _handle = core.BNCreateCustomLanguageRepresentationFunction(arch.handle, owner.handle, hlil.handle, self._cb) + assert _handle is not None + else: + self.comment_start_string = core.BNGetLanguageRepresentationFunctionCommentStartString(handle) + self.comment_end_string = core.BNGetLanguageRepresentationFunctionCommentEndString(handle) + self.annotation_start_string = core.BNGetLanguageRepresentationFunctionAnnotationStartString(handle) + self.annotation_end_string = core.BNGetLanguageRepresentationFunctionAnnotationEndString(handle) + _handle = handle + assert _handle is not None + self.handle: core.BNLanguageRepresentationFunctionHandle = _handle + + def __del__(self): + if core is not None: + core.BNFreeLanguageRepresentationFunction(self.handle) + + def _external_ref_taken(self, ctxt): + try: + self.__class__._registered_instances.append(self) + except: + log_error(traceback.format_exc()) + + def _external_ref_released(self, ctxt): + try: + self.__class__._registered_instances.remove(self) + except: + log_error(traceback.format_exc()) + + def _init_token_emitter(self, ctxt, emitter: core.BNHighLevelILTokenEmitterHandle): + try: + emitter = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(emitter)) + self.perform_init_token_emitter(emitter) + except: + log_error(traceback.format_exc()) + + def _get_expr_text( + self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int, + tokens: core.BNHighLevelILTokenEmitterHandle, settings: Optional[core.BNDisassemblySettingsHandle], + as_full_ast: bool, precedence: OperatorPrecedence, statement: bool + ): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index)) + tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) + if settings is not None: + settings = function.DisassemblySettings(core.BNNewDisassemblySettingsReference(settings)) + self.perform_get_expr_text(instr, tokens, settings, as_full_ast, precedence, statement) + except: + log_error(traceback.format_exc()) + + def _begin_lines( + self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int, + tokens: core.BNHighLevelILTokenEmitterHandle + ): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index)) + tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) + self.perform_begin_lines(instr, tokens) + except: + log_error(traceback.format_exc()) + + def _end_lines( + self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int, + tokens: core.BNHighLevelILTokenEmitterHandle + ): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index)) + tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) + self.perform_end_lines(instr, tokens) + except: + log_error(traceback.format_exc()) + + def _comment_start_string(self, ctxt): + try: + return core.BNAllocString(self.comment_start_string) + except: + log_error(traceback.format_exc()) + return core.BNAllocString("// ") + + def _comment_end_string(self, ctxt): + try: + return core.BNAllocString(self.comment_end_string) + except: + log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _annotation_start_string(self, ctxt): + try: + return core.BNAllocString(self.annotation_start_string) + except: + log_error(traceback.format_exc()) + return core.BNAllocString("{") + + def _annotation_end_string(self, ctxt): + try: + return core.BNAllocString(self.annotation_end_string) + except: + log_error(traceback.format_exc()) + return core.BNAllocString("}") + + def perform_init_token_emitter(self, emitter: HighLevelILTokenEmitter): + """Override this method to initialize the options for the token emitter before it is used.""" + pass + + def perform_get_expr_text( + self, instr: 'highlevelil.HighLevelILInstruction', tokens: HighLevelILTokenEmitter, + settings: Optional['function.DisassemblySettings'], as_full_ast: bool = True, + precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence, statement: bool = False + ): + """ + This method must be overridden by all language representation plugins. + + This method is called to emit the tokens for a given High Level IL instruction. + """ + raise NotImplementedError + + def perform_begin_lines(self, instr: highlevelil.HighLevelILInstruction, tokens: HighLevelILTokenEmitter): + """This method can be overridden to emit tokens at the start of a function.""" + pass + + def perform_end_lines(self, instr: highlevelil.HighLevelILInstruction, tokens: HighLevelILTokenEmitter): + """This method can be overridden to emit tokens at the end of a function.""" + pass + + def get_expr_text( + self, instr: 'highlevelil.HighLevelILInstruction', settings: Optional['function.DisassemblySettings'], + as_full_ast: bool = True, precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence, + statement: bool = False + ) -> List['function.DisassemblyTextLine']: + """Gets the lines of tokens for a given High Level IL instruction.""" + count = ctypes.c_ulonglong() + if settings is not None: + settings = settings.handle + lines = core.BNGetLanguageRepresentationFunctionExprText(self.handle, instr.function.handle, instr.expr_index, + settings, as_full_ast, precedence, statement, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + if lines[i].instrIndex != 0xffffffffffffffff: + il_instr = instr.function[lines[i].instrIndex] # type: ignore + else: + il_instr = None + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + def get_linear_lines( + self, instr: 'highlevelil.HighLevelILInstruction', + settings: Optional['function.DisassemblySettings'] = None, + as_full_ast: bool = True + ) -> List['function.DisassemblyTextLine']: + """ + Generates lines for the given High Level IL instruction in the style of the linear view. To get the lines + for the entire function, pass the ``root`` property of a ``class HighLevelILFunction``. + """ + count = ctypes.c_ulonglong() + if settings is not None: + settings = settings.handle + lines = core.BNGetLanguageRepresentationFunctionLinearLines(self.handle, instr.function.handle, instr.expr_index, + settings, as_full_ast, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + if lines[i].instrIndex != 0xffffffffffffffff: + il_instr = instr.function[lines[i].instrIndex] # type: ignore + else: + il_instr = None + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + def get_block_lines( + self, block: 'highlevelil.HighLevelILBasicBlock', settings: Optional['function.DisassemblySettings'] = None + ) -> List['function.DisassemblyTextLine']: + """Generates lines for a single High Level IL basic block.""" + count = ctypes.c_ulonglong() + if settings is not None: + settings = settings.handle + lines = core.BNGetLanguageRepresentationFunctionBlockLines(self.handle, block.handle, settings, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + if lines[i].instrIndex != 0xffffffffffffffff: + il_instr = instr.function[lines[i].instrIndex] # type: ignore + else: + il_instr = None + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + @property + def arch(self) -> 'architecture.Architecture': + return architecture.CoreArchitecture._from_cache(core.BNGetLanguageRepresentationArchitecture(self.handle)) + + @property + def function(self) -> 'function.Function': + return function.Function(handle=core.BNGetLanguageRepresentationOwnerFunction(self.handle)) + + @property + def high_level_il(self) -> 'highlevelil.HighLevelILFunction': + return self.hlil + + @property + def hlil(self) -> 'highlevelil.HighLevelILFunction': + return highlevelil.HighLevelILFunction(arch=self.arch, + handle=core.BNGetLanguageRepresentationILFunction(self.handle), source_func=self.function) + + +class _LanguageRepresentationFunctionTypeMetaClass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetLanguageRepresentationFunctionTypeList(count) + assert types is not None, "core.BNGetLanguageRepresentationFunctionTypeList returned None" + try: + for i in range(0, count.value): + yield CoreLanguageRepresentationFunctionType(handle=types[i]) + finally: + core.BNFreeLanguageRepresentationFunctionTypeList(types) + + def __getitem__(cls, value): + binaryninja._init_plugins() + lang = core.BNGetLanguageRepresentationFunctionTypeByName(str(value)) + if lang is None: + raise KeyError("'%s' is not a valid language" % str(value)) + return CoreLanguageRepresentationFunctionType(handle=lang) + + +class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFunctionTypeMetaClass): + """ + ``class LanguageRepresentationFunctionType`` represents a custom language representation function type. + This class provides methods to create ``class LanguageRepresentationFunction`` instances for functions, as well + as manage the printing and parsing of types. + """ + _registered_languages = [] + language_name = None + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNLanguageRepresentationFunctionType) + + def register(self): + """Registers the language representation function type.""" + if self.__class__.language_name is None: + raise ValueError("Language name is missing") + self._cb = core.BNCustomLanguageRepresentationFunctionType() + self._cb.context = 0 + self._cb.create = self._cb.create.__class__(self._create) + self._cb.isValid = self._cb.isValid.__class__(self._is_valid) + self._cb.getTypePrinter = self._cb.getTypePrinter.__class__(self._type_printer) + self._cb.getTypeParser = self._cb.getTypeParser.__class__(self._type_parser) + self._cb.getFunctionTypeTokens = self._cb.getFunctionTypeTokens.__class__(self._function_type_tokens) + self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines) + self.handle = core.BNRegisterLanguageRepresentationFunctionType(self.__class__.language_name, self._cb) + self.__class__._registered_languages.append(self) + + def _create( + self, ctxt, arch: core.BNArchitectureHandle, owner: core.BNFunctionHandle, + hlil: core.BNHighLevelILFunctionHandle + ): + try: + arch = architecture.CoreArchitecture._from_cache(arch) + owner = function.Function(handle=core.BNNewFunctionReference(owner)) + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + result = self.create(arch, owner, hlil) + if result is None: + raise ValueError(f"create returned None for language representation '{self.name}'") + handle = core.BNNewLanguageRepresentationFunctionReference(result.handle) + assert handle is not None, "core.BNNewLanguageRepresentationFunctionReference returned None" + return ctypes.cast(handle, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def _is_valid(self, ctxt, view: core.BNBinaryViewHandle) -> bool: + try: + view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + return self.is_valid(view) + except: + log_error(traceback.format_exc()) + return False + + def _type_printer(self, ctxt): + try: + result = self.type_printer + if result is None: + return None + return ctypes.cast(result.handle, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def _type_parser(self, ctxt): + try: + result = self.type_parser + if result is None: + return None + return ctypes.cast(result.handle, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def _function_type_tokens( + self, ctxt, func: core.BNFunctionHandle, settings: Optional[core.BNDisassemblySettingsHandle], + count: ctypes.POINTER(ctypes.c_ulonglong) + ): + try: + func = function.Function(handle=core.BNNewFunctionReference(func)) + if settings is not None: + settings = function.DisassemblySettings(handle=core.BNNewDisassemblySettingsReference(settings)) + lines = self.function_type_tokens(func, settings) + + count[0] = len(lines) + self.line_buf = (core.BNDisassemblyTextLine * len(lines))() + for i in range(len(lines)): + line = lines[i] + color = line.highlight + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) + self.line_buf[i].highlight = color._to_core_struct() + if line.address is None: + if len(line.tokens) > 0: + self.line_buf[i].addr = line.tokens[0].address + else: + self.line_buf[i].addr = 0 + else: + self.line_buf[i].addr = line.address + if line.il_instruction is not None: + self.line_buf[i].instrIndex = line.il_instruction.instr_index + else: + self.line_buf[i].instrIndex = 0xffffffffffffffff + + self.line_buf[i].count = len(line.tokens) + self.line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens) + + return ctypes.cast(self.line_buf, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_lines(self, ctxt, lines, count): + self.line_buf = None + + @property + def name(self) -> str: + if hasattr(self, 'handle'): + return core.BNGetLanguageRepresentationFunctionTypeName(self.handle) + return self.__class__.language_name + + def create( + self, arch: 'architecture.Architecture', owner: 'function.Function', hlil: 'highlevelil.HighLevelILFunction' + ) -> LanguageRepresentationFunction: + """ + This method must be overridden. This creates the ``class LanguageRepresentationFunction`` object for the + given architecture, owner function, and High Level IL function. + """ + raise NotImplementedError + + def is_valid(self, view: 'binaryview.BinaryView') -> bool: + """Returns whether the language is valid for the given binary view.""" + return True + + @property + def type_printer(self) -> Optional['binaryninja.typeprinter.TypePrinter']: + """ + Returns the type printer for displaying types in this language. If ``None`` is returned, the default type + printer will be used. + """ + return None + + @property + def type_parser(self) -> Optional['binaryninja.typeparser.TypeParser']: + """ + Returns the type parser for parsing types in this language. If ``None`` is returned, the default type + parser will be used. + """ + return None + + def function_type_tokens( + self, func: 'function.Function', settings: Optional['function.DisassemblySettings'] + ) -> List['function.DisassemblyTextLine']: + """ + Returns a list of lines representing a function prototype in this language. If no lines are returned, the + default C-style prototype will be used. + """ + return [] + + def __repr__(self): + return f"<LanguageRepresentationFunctionType: {self.name}>" + + +_language_cache = {} + + +class CoreLanguageRepresentationFunctionType(LanguageRepresentationFunctionType): + def __init__(self, handle: core.BNLanguageRepresentationFunctionTypeHandle): + super(CoreLanguageRepresentationFunctionType, self).__init__(handle=handle) + if type(self) is CoreLanguageRepresentationFunctionType: + global _language_cache + _language_cache[ctypes.addressof(handle.contents)] = self + + @classmethod + def _from_cache(cls, handle) -> 'LanguageRepresentationFunctionType': + """ + Look up a representation type from a given BNLanguageRepresentationFunctionType handle + :param handle: BNLanguageRepresentationFunctionType pointer + :return: Respresentation type instance responsible for this handle + """ + global _language_cache + return _language_cache.get(ctypes.addressof(handle.contents)) or cls(handle) + + def create( + self, arch: 'architecture.Architecture', owner: 'function.Function', hlil: 'highlevelil.HighLevelILFunction' + ) -> LanguageRepresentationFunction: + raise NotImplementedError + + def is_valid(self, view: 'binaryview.BinaryView') -> bool: + return core.BNIsLanguageRepresentationFunctionTypeValid(self.handle, view.handle) + + @property + def type_printer(self) -> Optional['binaryninja.typeprinter.TypePrinter']: + result = core.BNGetLanguageRepresentationFunctionTypePrinter(self.handle) + if result is None: + return None + return binaryninja.typeprinter.TypePrinter(handle=result) + + @property + def type_parser(self) -> Optional['binaryninja.typeparser.TypeParser']: + result = core.BNGetLanguageRepresentationFunctionTypeParser(self.handle) + if result is None: + return None + return binaryninja.typeparser.TypeParser(handle=result) + + def function_type_tokens( + self, func: 'function.Function', settings: Optional['function.DisassemblySettings'] + ) -> List['function.DisassemblyTextLine']: + count = ctypes.c_ulonglong() + if settings is not None: + settings = settings.handle + lines = core.BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens(self.handle, func.handle, settings, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, color=color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result diff --git a/python/lineardisassembly.py b/python/lineardisassembly.py index a19148ca..40a0d6c5 100644 --- a/python/lineardisassembly.py +++ b/python/lineardisassembly.py @@ -345,12 +345,13 @@ class LinearViewObject: @staticmethod def language_representation( - view: 'binaryview.BinaryView', settings: Optional['_function.DisassemblySettings'] = None + view: 'binaryview.BinaryView', settings: Optional['_function.DisassemblySettings'] = None, + language: str = "Pseudo C" ) -> 'LinearViewObject': _settings = settings if _settings is not None: _settings = _settings.handle - return LinearViewObject(core.BNCreateLinearViewLanguageRepresentation(view.handle, _settings)) + return LinearViewObject(core.BNCreateLinearViewLanguageRepresentation(view.handle, _settings, language)) @staticmethod def data_only( @@ -453,12 +454,13 @@ class LinearViewObject: @staticmethod def single_function_language_representation( - func: '_function.Function', settings: Optional['_function.DisassemblySettings'] = None + func: '_function.Function', settings: Optional['_function.DisassemblySettings'] = None, + language: str = "Pseudo C" ) -> 'LinearViewObject': _settings = settings if _settings is not None: _settings = _settings.handle - return LinearViewObject(core.BNCreateLinearViewSingleFunctionLanguageRepresentation(func.handle, _settings)) + return LinearViewObject(core.BNCreateLinearViewSingleFunctionLanguageRepresentation(func.handle, _settings, language)) class LinearViewCursor: diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index 02b070fa..4b76939f 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -1809,23 +1809,23 @@ def _get_current_il_function(instance: PythonScriptingInstance): from binaryninja import FunctionGraphType if instance.interpreter.locals["current_ui_view_location"] is not None: ilType = instance.interpreter.locals["current_ui_view_location"].getILViewType() - if ilType == FunctionGraphType.LowLevelILFunctionGraph: + if ilType.view_type == FunctionGraphType.LowLevelILFunctionGraph: return instance.interpreter.locals["current_llil"] - elif ilType == FunctionGraphType.LiftedILFunctionGraph: + elif ilType.view_type == FunctionGraphType.LiftedILFunctionGraph: return instance.interpreter.locals["current_lifted_il"] - elif ilType == FunctionGraphType.LowLevelILSSAFormFunctionGraph: + elif ilType.view_type == FunctionGraphType.LowLevelILSSAFormFunctionGraph: return instance.interpreter.locals["current_llil_ssa"] - elif ilType == FunctionGraphType.MappedMediumLevelILFunctionGraph: + elif ilType.view_type == FunctionGraphType.MappedMediumLevelILFunctionGraph: return instance.interpreter.locals["current_mapped_mlil"] - elif ilType == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph: + elif ilType.view_type == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph: return instance.interpreter.locals["current_mapped_mlil_ssa"] - elif ilType == FunctionGraphType.MediumLevelILFunctionGraph: + elif ilType.view_type == FunctionGraphType.MediumLevelILFunctionGraph: return instance.interpreter.locals["current_mlil"] - elif ilType == FunctionGraphType.MediumLevelILSSAFormFunctionGraph: + elif ilType.view_type == FunctionGraphType.MediumLevelILSSAFormFunctionGraph: return instance.interpreter.locals["current_mlil_ssa"] - elif ilType == FunctionGraphType.HighLevelILFunctionGraph: + elif ilType.view_type == FunctionGraphType.HighLevelILFunctionGraph: return instance.interpreter.locals["current_hlil"] - elif ilType == FunctionGraphType.HighLevelILSSAFormFunctionGraph: + elif ilType.view_type == FunctionGraphType.HighLevelILSSAFormFunctionGraph: return instance.interpreter.locals["current_hlil_ssa"] return None diff --git a/python/types.py b/python/types.py index 2c2b652f..d5fd3544 100644 --- a/python/types.py +++ b/python/types.py @@ -20,7 +20,7 @@ import ctypes import typing -from typing import Generator, List, Union, Tuple, Optional, Iterable, Dict, Generic, TypeVar +from typing import Generator, List, Union, Tuple, Optional, Iterable, Dict, Generic, TypeVar, Callable from dataclasses import dataclass import uuid @@ -53,6 +53,7 @@ SomeType = Union['TypeBuilder', 'Type'] TypeContainerType = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary'] NameSpaceType = Optional[Union[str, List[str], 'NameSpace']] TypeParserResult = typeparser.TypeParserResult +ResolveMemberCallback = Callable[['NamedTypeReferenceType', 'StructureType', int, int, int, 'StructureMember'], None] # 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 @@ -384,6 +385,12 @@ class CoreSymbol: def handle(self): return self._handle + def imported_function_from_import_address_symbol(self, addr: int) -> Optional['CoreSymbol']: + sym = core.BNImportedFunctionFromImportAddressSymbol(self._handle, addr) + if sym is None: + return None + return CoreSymbol(sym) + class Symbol(CoreSymbol): """ @@ -2717,6 +2724,40 @@ class StructureType(Type): ntr_type, guid, name, self.alignment, self.width, self.platform, self.confidence ) + def resolve_member_or_base_member( + self, view: Optional['binaryview.BinaryView'], offset: int, size: int, + resolve_func: ResolveMemberCallback, member_index_hint: Optional[int] = None + ) -> bool: + if view is not None: + view = view.handle + + def resolve_member_callback( + ctxt, base_name: core.BNNamedTypeReferenceHandle, resolved_struct: core.BNStructureHandle, + member_index: int, struct_offset: int, adjusted_offset: int, member: core.BNStructureMember + ): + if base_name: + base_name = NamedTypeReferenceType.create_from_handle(core.BNNewNamedTypeReference(base_name)) + if resolved_struct: + resolved_struct = StructureType.from_core_struct(core.BNNewStructureReference(resolved_struct)) + t = Type.create(core.BNNewTypeReference(member.type), confidence=member.typeConfidence) + struct_member = StructureMember( + t, member.name, member.offset, MemberAccess(member.access), MemberScope(member.scope) + ) + resolve_func(base_name, resolved_struct, member_index, struct_offset, adjusted_offset, struct_member) + + member_index_hint_value = 0 + if member_index_hint is not None: + member_index_hint_value = member_index_hint + return core.BNResolveStructureMemberOrBaseMember( + self.struct_handle, view, offset, size, None, + ctypes.CFUNCTYPE( + None, ctypes.c_void_p, core.BNNamedTypeReferenceHandle, core.BNStructureHandle, ctypes.c_size_t, + ctypes.c_uint64, ctypes.c_uint64, core.BNStructureMember + )(resolve_member_callback), + member_index_hint is not None, + member_index_hint_value + ) + @property def children(self) -> List[Type]: return [member.type for member in self.members] @@ -3064,6 +3105,30 @@ class FunctionType(Type): return result @property + def parameters_with_all_locations(self) -> List[FunctionParameter]: + """Type parameters list with default locations filled in with values (read-only)""" + count = ctypes.c_ulonglong() + params = core.BNGetTypeParameters(self._handle, count) + assert params is not None, "core.BNGetTypeParameters returned None" + result = [] + for i in range(0, count.value): + param_type = Type.create( + core.BNNewTypeReference(params[i].type), platform=self._platform, confidence=params[i].typeConfidence + ) + name = params[i].name + if (params[i].location.type + == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None): + name = self._platform.arch.get_reg_name(params[i].location.storage) + elif params[i].location.type == VariableSourceType.StackVariableSourceType: + name = "arg_%x" % params[i].location.storage + param_location = variable.VariableNameAndType( + params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type + ) + result.append(FunctionParameter(param_type, params[i].name, param_location)) + core.BNFreeTypeParameterList(params, count.value) + return result + + @property def has_variable_arguments(self) -> BoolWithConfidence: """Whether type has variable arguments (read-only)""" result = core.BNTypeHasVariableArguments(self._handle) diff --git a/python/variable.py b/python/variable.py index 8ed3f545..7b4121bf 100644 --- a/python/variable.py +++ b/python/variable.py @@ -20,14 +20,14 @@ # IN THE SOFTWARE. import ctypes -from typing import List, Generator, Optional, Union, Set, Dict +from typing import List, Generator, Optional, Union, Set, Dict, Tuple from dataclasses import dataclass import binaryninja from . import _binaryninjacore as core from . import databuffer from . import decorators -from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType +from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType, BuiltinType FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction", "binaryninja.mediumlevelil.MediumLevelILFunction", @@ -223,6 +223,12 @@ class ConstantData(RegisterValue): raise ValueError(f"ConstantData requires a Function instance: {self.size}") return self.function.get_constant_data(self.type, self.value, self.size) + @property + def data_and_builtin(self) -> Tuple[databuffer.DataBuffer, BuiltinType]: + if self.function is None: + raise ValueError(f"ConstantData requires a Function instance: {self.size}") + return self.function.get_constant_data_and_builtin(self.type, self.value, self.size) + @dataclass(frozen=True) class ValueRange: diff --git a/rust/examples/decompile/src/main.rs b/rust/examples/decompile/src/main.rs index cac5ae06..f4f2a0ff 100644 --- a/rust/examples/decompile/src/main.rs +++ b/rust/examples/decompile/src/main.rs @@ -18,7 +18,7 @@ fn decompile_to_c(view: &BinaryView, func: &Function) { settings.set_option(DisassemblyOption::ShowAddress, false); settings.set_option(DisassemblyOption::WaitForIL, true); - let linearview = LinearViewObject::language_representation(view, &settings); + let linearview = LinearViewObject::language_representation(view, &settings, "Pseudo C"); let mut cursor = LinearViewCursor::new(&linearview); cursor.seek_to_address(func.highest_address()); diff --git a/rust/src/databuffer.rs b/rust/src/databuffer.rs index c3d268cd..29d7636b 100644 --- a/rust/src/databuffer.rs +++ b/rust/src/databuffer.rs @@ -109,8 +109,8 @@ impl DataBuffer { } } - pub fn to_escaped_string(&self, null_terminates: bool) -> BnString { - unsafe { BnString::from_raw(BNDataBufferToEscapedString(self.0, null_terminates)) } + pub fn to_escaped_string(&self, null_terminates: bool, escape_printable: bool) -> BnString { + unsafe { BnString::from_raw(BNDataBufferToEscapedString(self.0, null_terminates, escape_printable)) } } pub fn from_escaped_string(value: &BnString) -> Self { diff --git a/rust/src/function.rs b/rust/src/function.rs index 01b086f2..0508ca4d 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -39,6 +39,7 @@ use crate::{databuffer::DataBuffer, disassembly::InstructionTextToken, rc::*}; pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason; pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride; pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType; +pub use binaryninjacore_sys::BNBuiltinType as BuiltinType; use std::{fmt, mem}; use std::{ffi::c_char, hash::Hash, ops::Range}; @@ -124,6 +125,78 @@ impl BlockContext for NativeBlock { } } +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum FunctionViewType { + Normal, + LowLevelIL, + LiftedIL, + LowLevelILSSAForm, + MediumLevelIL, + MediumLevelILSSAForm, + MappedMediumLevelIL, + MappedMediumLevelILSSAForm, + HighLevelIL, + HighLevelILSSAForm, + HighLevelLanguageRepresentation(String), +} + +pub(crate) struct RawFunctionViewType(pub BNFunctionViewType); + +impl FunctionViewType { + pub(crate) fn as_raw(&self) -> RawFunctionViewType { + let view_type = match self { + FunctionViewType::Normal => BNFunctionGraphType::NormalFunctionGraph, + FunctionViewType::LowLevelIL => BNFunctionGraphType::LowLevelILFunctionGraph, + FunctionViewType::LiftedIL => BNFunctionGraphType::LiftedILFunctionGraph, + FunctionViewType::LowLevelILSSAForm => BNFunctionGraphType::LowLevelILSSAFormFunctionGraph, + FunctionViewType::MediumLevelIL => BNFunctionGraphType::MediumLevelILFunctionGraph, + FunctionViewType::MediumLevelILSSAForm => BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph, + FunctionViewType::MappedMediumLevelIL => BNFunctionGraphType::MappedMediumLevelILFunctionGraph, + FunctionViewType::MappedMediumLevelILSSAForm => BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph, + FunctionViewType::HighLevelIL => BNFunctionGraphType::HighLevelILFunctionGraph, + FunctionViewType::HighLevelILSSAForm => BNFunctionGraphType::HighLevelILSSAFormFunctionGraph, + FunctionViewType::HighLevelLanguageRepresentation(_) => BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph, + }; + RawFunctionViewType(BNFunctionViewType { + type_: view_type, + name: if let FunctionViewType::HighLevelLanguageRepresentation(ref name) = self { + std::ffi::CString::new(name.to_string()).unwrap().into_raw() + } else { + std::ptr::null() + }, + }) + } +} + +impl Into<FunctionViewType> for FunctionGraphType { + fn into(self) -> FunctionViewType { + match self { + BNFunctionGraphType::LowLevelILFunctionGraph => FunctionViewType::LowLevelIL, + BNFunctionGraphType::LiftedILFunctionGraph => FunctionViewType::LiftedIL, + BNFunctionGraphType::LowLevelILSSAFormFunctionGraph => FunctionViewType::LowLevelILSSAForm, + BNFunctionGraphType::MediumLevelILFunctionGraph => FunctionViewType::MediumLevelIL, + BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph => FunctionViewType::MediumLevelILSSAForm, + BNFunctionGraphType::MappedMediumLevelILFunctionGraph => FunctionViewType::MappedMediumLevelIL, + BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph => FunctionViewType::MappedMediumLevelILSSAForm, + BNFunctionGraphType::HighLevelILFunctionGraph => FunctionViewType::HighLevelIL, + BNFunctionGraphType::HighLevelILSSAFormFunctionGraph => FunctionViewType::HighLevelILSSAForm, + BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph => { + FunctionViewType::HighLevelLanguageRepresentation("Pseudo C".into() + ) + } + _ => FunctionViewType::Normal, + } + } +} + +impl Drop for RawFunctionViewType { + fn drop(&mut self) { + if !self.0.name.is_null() { + unsafe { let _ = std::ffi::CString::from_raw(self.0.name as *mut _); } + } + } +} + #[derive(Eq)] pub struct Function { pub(crate) handle: *mut BNFunction, @@ -1205,10 +1278,12 @@ impl Function { state: RegisterValueType, value: u64, size: Option<usize>, - ) -> DataBuffer { + ) -> (DataBuffer, BuiltinType) { let size = size.unwrap_or(0); let state_raw = state.into_raw_value(); - DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state_raw, value, size) }) + let mut builtin_type = BuiltinType::BuiltinNone; + let buffer = DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state_raw, value, size, &mut builtin_type) }); + (buffer, builtin_type) } pub fn constants_referenced_by( @@ -2141,11 +2216,11 @@ impl Function { pub fn create_graph( &self, - graph_type: FunctionGraphType, + view_type: FunctionViewType, settings: Option<DisassemblySettings>, ) -> Ref<FlowGraph> { let settings_raw = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut()); - let result = unsafe { BNCreateFunctionGraph(self.handle, graph_type, settings_raw) }; + let result = unsafe { BNCreateFunctionGraph(self.handle, view_type.as_raw().0, settings_raw) }; unsafe { Ref::new(FlowGraph::from_raw(result)) } } diff --git a/rust/src/linearview.rs b/rust/src/linearview.rs index 84b4323e..b90d2784 100644 --- a/rust/src/linearview.rs +++ b/rust/src/linearview.rs @@ -103,11 +103,17 @@ impl LinearViewObject { } } - pub fn language_representation(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> { + pub fn language_representation( + view: &BinaryView, + settings: &DisassemblySettings, + language: &str, + ) -> Ref<Self> { unsafe { + let language = std::ffi::CString::new(language).unwrap(); let handle = binaryninjacore_sys::BNCreateLinearViewLanguageRepresentation( view.handle, settings.handle, + language.as_ptr(), ); Self::from_raw(handle) @@ -195,12 +201,15 @@ impl LinearViewObject { pub fn single_function_language_representation( function: &Function, settings: &DisassemblySettings, + language: &str, ) -> Ref<Self> { unsafe { + let language = std::ffi::CString::new(language).unwrap(); let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionLanguageRepresentation( function.handle, settings.handle, + language.as_ptr(), ); Self::from_raw(handle) @@ -1116,6 +1116,19 @@ BNIntegerDisplayType Type::GetIntegerTypeDisplayType() const return BNGetIntegerTypeDisplayType(m_object); } + +BNNameType Type::GetNameType() const +{ + return BNTypeGetNameType(m_object); +} + + +bool Type::ShouldDisplayReturnType() const +{ + return BNTypeShouldDisplayReturnType(m_object); +} + + string Type::GenerateAutoTypeId(const string& source, const QualifiedName& name) { BNQualifiedName nameObj = name.GetAPIObject(); @@ -1215,6 +1228,15 @@ Ref<NamedTypeReference> Type::GetRegisteredName() const } +string Type::GetAlternateName() const +{ + char* name = BNGetTypeAlternateName(m_object); + string result = name; + BNFreeString(name); + return result; +} + + Ref<Type> Type::WithReplacedStructure(Structure* from, Structure* to) { BNType* result = BNTypeWithReplacedStructure(m_object, from->GetObject(), to->GetObject()); @@ -2544,6 +2566,40 @@ Ref<Structure> Structure::WithReplacedNamedTypeReference(NamedTypeReference* fro } +struct ResolveMemberCallbackInfo +{ + const std::function<void(NamedTypeReference*, Structure*, size_t, + uint64_t, uint64_t, const StructureMember&)>* callback; +}; + + +static void ResolveMemberCallback(void* ctxt, BNNamedTypeReference* baseName, BNStructure* resolvedStruct, + size_t memberIndex, uint64_t structOffset, uint64_t adjustedOffset, BNStructureMember member) +{ + ResolveMemberCallbackInfo* resolveFunc = (ResolveMemberCallbackInfo*)ctxt; + Ref<NamedTypeReference> baseNameRef = baseName ? new NamedTypeReference(BNNewNamedTypeReference(baseName)) : nullptr; + Ref<Structure> resolvedStructRef = resolvedStruct ? new Structure(BNNewStructureReference(resolvedStruct)) : nullptr; + StructureMember apiMember; + apiMember.type = Confidence<Ref<Type>>(new Type(BNNewTypeReference(member.type)), member.typeConfidence); + apiMember.name = member.name; + apiMember.offset = member.offset; + apiMember.access = member.access; + apiMember.scope = member.scope; + (*resolveFunc->callback)(baseNameRef, resolvedStructRef, memberIndex, structOffset, adjustedOffset, apiMember); +} + + +bool Structure::ResolveMemberOrBaseMember(BinaryView* data, uint64_t offset, size_t size, + const std::function<void(NamedTypeReference* baseName, Structure* s, size_t memberIndex, + uint64_t structOffset, uint64_t adjustedOffset, const StructureMember& member)>& resolveFunc, + std::optional<size_t> memberIndexHint) +{ + ResolveMemberCallbackInfo callbackInfo = { &resolveFunc }; + return BNResolveStructureMemberOrBaseMember(m_object, data ? data->GetObject() : nullptr, offset, size, + &callbackInfo, ResolveMemberCallback, memberIndexHint.has_value(), memberIndexHint.value_or(0)); +} + + StructureBuilder::StructureBuilder() { m_object = BNCreateStructureBuilder(); diff --git a/ui/commands.h b/ui/commands.h index 70ed5778..37ed068a 100644 --- a/ui/commands.h +++ b/ui/commands.h @@ -27,11 +27,11 @@ bool BINARYNINJAUIAPI askForNewType(QWidget* parent, std::optional<BinaryNinja:: bool BINARYNINJAUIAPI inputNewType(QWidget* parent, BinaryViewRef data, FunctionRef currentFunction, uint64_t currentAddr, size_t selectionSize, HighlightTokenState& highlight); bool BINARYNINJAUIAPI createInferredMember(QWidget* parent, BinaryViewRef data, HighlightTokenState& highlight, - FunctionRef func, BNFunctionGraphType ilType, size_t instrIndex); + FunctionRef func, const BinaryNinja::FunctionViewType& ilType, size_t instrIndex); bool BINARYNINJAUIAPI createStructMembers( QWidget* parent, BinaryViewRef data, HighlightTokenState& highlight, FunctionRef func); -bool BINARYNINJAUIAPI inputPossibleValueSet(QWidget* parent, BinaryViewRef data, FunctionRef currentFunction, BNFunctionGraphType funcType, +bool BINARYNINJAUIAPI inputPossibleValueSet(QWidget* parent, BinaryViewRef data, FunctionRef currentFunction, const BinaryNinja::FunctionViewType& funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex = BN_INVALID_EXPR); bool BINARYNINJAUIAPI getEnumSelection(QWidget* parent, BinaryViewRef data, FunctionRef func, uint64_t constValue, @@ -55,12 +55,12 @@ uint64_t BINARYNINJAUIAPI getInnerMostStructureOffset( std::string BINARYNINJAUIAPI createStructureName(BinaryNinja::TypeContainer types, const std::string& prefix = "struct_"); std::optional<BinaryNinja::Variable> BINARYNINJAUIAPI getSplitVariableForAssignment( - FunctionRef func, BNFunctionGraphType ilType, uint64_t location, const BinaryNinja::Variable& var); + FunctionRef func, const BinaryNinja::FunctionViewType& ilType, uint64_t location, const BinaryNinja::Variable& var); std::optional<size_t> getVariableDefinitionInstructionIndex( - FunctionRef func, BNFunctionGraphType funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex); + FunctionRef func, const BinaryNinja::FunctionViewType& funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex); std::optional<size_t> getVariableDefinitionAddress( - FunctionRef func, BNFunctionGraphType funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex); + FunctionRef func, const BinaryNinja::FunctionViewType& funcType, const BinaryNinja::Variable& var, size_t ilInstructionIndex); bool IsDefaultArgumentOrParameterName(const std::string& name); std::optional<std::string> GetVariableNameFromExpr(BinaryNinja::Function* func, diff --git a/ui/disassemblyview.h b/ui/disassemblyview.h index 3e0408ae..c7595f96 100644 --- a/ui/disassemblyview.h +++ b/ui/disassemblyview.h @@ -31,11 +31,11 @@ */ class BINARYNINJAUIAPI DisassemblyHistoryEntry : public FlowGraphHistoryEntry { - BNFunctionGraphType m_graphType; + BinaryNinja::FunctionViewType m_graphType; public: - BNFunctionGraphType getGraphType() const { return m_graphType; } - void setGraphType(BNFunctionGraphType type) { m_graphType = type; } + const BinaryNinja::FunctionViewType& getGraphType() const { return m_graphType; } + void setGraphType(const BinaryNinja::FunctionViewType& type) { m_graphType = type; } virtual Json::Value serialize() const override; virtual bool deserialize(const Json::Value& value) override; @@ -71,8 +71,8 @@ class BINARYNINJAUIAPI DisassemblyView : public FlowGraphWidget virtual ViewPaneHeaderSubtypeWidget* getHeaderSubtypeWidget() override; virtual QWidget* getHeaderOptionsWidget() override; - virtual BNFunctionGraphType getILViewType() override { return m_ilViewType; }; - virtual void setILViewType(BNFunctionGraphType ilViewType) override; + virtual BinaryNinja::FunctionViewType getILViewType() override { return m_ilViewType; }; + virtual void setILViewType(const BinaryNinja::FunctionViewType& ilViewType) override; void setOption(BNDisassemblyOption option, bool state = true); void toggleOption(BNDisassemblyOption option); @@ -132,7 +132,7 @@ class BINARYNINJAUIAPI DisassemblyView : public FlowGraphWidget void bindActions(); static void addOptionsMenuActions(Menu& menu); - BNFunctionGraphType m_ilViewType; + BinaryNinja::FunctionViewType m_ilViewType; std::set<BNDisassemblyOption> m_options; BNDisassemblyAddressMode m_addressMode; BNDisassemblyCallParameterHints m_callParamHints; @@ -202,7 +202,7 @@ class BINARYNINJAUIAPI DisassemblyFunctionHeader : public QWidget void updateFonts(); void setCurrentFunction(FunctionRef func); - void setILViewType(BNFunctionGraphType ilViewType); + void setILViewType(const BinaryNinja::FunctionViewType& ilViewType); void setHighlightToken(const HighlightTokenState& state); void notifyRefresh(); @@ -235,7 +235,7 @@ class BINARYNINJAUIAPI DisassemblyContainer : public QWidget, public ViewContain void updateFonts(); void refreshHeader(FunctionRef func); void setCurrentFunction(FunctionRef func); - void setILViewType(BNFunctionGraphType ilViewType); + void setILViewType(const BinaryNinja::FunctionViewType& ilViewType); void setHeaderHighlightToken(const HighlightTokenState& state); protected: diff --git a/ui/ilchooser.h b/ui/ilchooser.h index e57b82a6..561e264f 100644 --- a/ui/ilchooser.h +++ b/ui/ilchooser.h @@ -22,10 +22,10 @@ class BINARYNINJAUIAPI ILChooserWidget : public MenuHelper public: ILChooserWidget(QWidget* parent, UIActionHandler* handler, bool longDescription); - void updateStatus(BNFunctionGraphType current); + void updateStatus(const BinaryNinja::FunctionViewType& current); - static QString shortNameForILType(BNFunctionGraphType type); - static QString longNameForILType(BNFunctionGraphType type); + static QString shortNameForILType(const BinaryNinja::FunctionViewType& type); + static QString longNameForILType(const BinaryNinja::FunctionViewType& type); protected: virtual void showMenu() override; diff --git a/ui/linearview.h b/ui/linearview.h index 8dc49041..1c470cb6 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -125,12 +125,14 @@ class StickyHeader: public QWidget uint64_t m_gutterWidthChars; LinearViewLine m_line; + BinaryNinja::FunctionViewType m_viewType; QProgressIndicator* m_updateIndicator; public: StickyHeader(BinaryViewRef data, LinearView* parent); void updateLine(const LinearViewLine& line); + void updateViewType(const BinaryNinja::FunctionViewType& viewType); void updateFonts(); virtual void paintEvent(QPaintEvent* event) override; @@ -209,7 +211,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub SettingsRef m_settings; DisassemblySettingsRef m_options; - BNFunctionGraphType m_ilViewType, m_prevILViewType = InvalidILViewType; + BinaryNinja::FunctionViewType m_ilViewType, m_prevILViewType = InvalidILViewType; HexEditorHighlightState m_highlightState; bool m_singleFunctionView = false; @@ -476,8 +478,8 @@ public: virtual HighlightTokenState getHighlightTokenState() override { return m_highlight; } void setHighlightTokenState(const HighlightTokenState& hts); - virtual BNFunctionGraphType getILViewType() override { return m_ilViewType; }; - virtual void setILViewType(BNFunctionGraphType ilViewType) override; + virtual BinaryNinja::FunctionViewType getILViewType() override { return m_ilViewType; }; + virtual void setILViewType(const BinaryNinja::FunctionViewType& ilViewType) override; void setHighlightMode(HexEditorHighlightMode mode); void setColorMode(HexEditorColorMode mode); @@ -35,7 +35,7 @@ PlatformRef BINARYNINJAUIAPI getOrAskForPlatform(QWidget* parent, BinaryViewRef PlatformRef BINARYNINJAUIAPI getOrAskForPlatform(QWidget* parent, PlatformRef defaultValue); std::optional<std::string> BINARYNINJAUIAPI getStringForGraphType(BNFunctionGraphType type); -std::optional<BNFunctionGraphType> BINARYNINJAUIAPI getGraphTypeForString(const std::string& type); +std::optional<BinaryNinja::FunctionViewType> BINARYNINJAUIAPI getGraphTypeForString(const std::string& type); namespace fmt { diff --git a/ui/variablelist.h b/ui/variablelist.h index 2b7ed8e7..e10d40eb 100644 --- a/ui/variablelist.h +++ b/ui/variablelist.h @@ -101,7 +101,7 @@ class BINARYNINJAUIAPI VariableListModel : public QAbstractListModel ViewFrame* m_view; BinaryViewRef m_data; FunctionRef m_func; - BNFunctionGraphType m_funcType; + BinaryNinja::FunctionViewType m_funcType; bool m_funcExceedsComplexity = false; BinaryNinja::AdvancedFunctionAnalysisDataRequestor m_analysisRequestor; std::vector<VariableListItem> m_items; @@ -123,13 +123,13 @@ class BINARYNINJAUIAPI VariableListModel : public QAbstractListModel FunctionRef function() const; //! Get the current function type. - BNFunctionGraphType functionType() const; + const BinaryNinja::FunctionViewType& functionType() const; //! Whether or not the function exceeds the set complexity threshold bool functionExceedsComplexity() const { return m_funcExceedsComplexity; } //! Set the focused function and update the content of the list. - void setFunction(FunctionRef func, BNFunctionGraphType il, const HighlightTokenState& hts); + void setFunction(FunctionRef func, const BinaryNinja::FunctionViewType& il, const HighlightTokenState& hts); //! Set the selection model, should correspond to the parent widget's. void setSelectionModel(QItemSelectionModel* model); diff --git a/ui/viewframe.h b/ui/viewframe.h index 260939f0..234e01c9 100644 --- a/ui/viewframe.h +++ b/ui/viewframe.h @@ -150,21 +150,21 @@ class BINARYNINJAUIAPI View virtual bool findNextData(uint64_t start, uint64_t end, const BinaryNinja::DataBuffer& data, uint64_t& addr, BNFindFlag flags, const std::function<bool(size_t current, size_t total)>& cb); virtual bool findNextText(uint64_t start, uint64_t end, const std::string& text, uint64_t& addr, - DisassemblySettingsRef settings, BNFindFlag flags, BNFunctionGraphType graph, + DisassemblySettingsRef settings, BNFindFlag flags, const BinaryNinja::FunctionViewType& graph, const std::function<bool(size_t current, size_t total)>& cb); virtual bool findNextConstant(uint64_t start, uint64_t end, uint64_t constant, uint64_t& addr, - DisassemblySettingsRef settings, BNFunctionGraphType graph, + DisassemblySettingsRef settings, const BinaryNinja::FunctionViewType& graph, const std::function<bool(size_t current, size_t total)>& cb); virtual bool findAllData(uint64_t start, uint64_t end, const BinaryNinja::DataBuffer& data, BNFindFlag flags, const std::function<bool(size_t current, size_t total)>& cb, const std::function<bool(uint64_t addr, const BinaryNinja::DataBuffer& match)>& matchCallback); virtual bool findAllText(uint64_t start, uint64_t end, const std::string& data, DisassemblySettingsRef settings, - BNFindFlag flags, BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& cb, + BNFindFlag flags, const BinaryNinja::FunctionViewType& graph, const std::function<bool(size_t current, size_t total)>& cb, const std::function<bool( uint64_t addr, const std::string& match, const BinaryNinja::LinearDisassemblyLine& line)>& matchCallback); virtual bool findAllConstant(uint64_t start, uint64_t end, uint64_t constant, DisassemblySettingsRef settings, - BNFunctionGraphType graph, const std::function<bool(size_t current, size_t total)>& cb, + const BinaryNinja::FunctionViewType& graph, const std::function<bool(size_t current, size_t total)>& cb, const std::function<bool(uint64_t addr, const BinaryNinja::LinearDisassemblyLine& line)>& matchCallback); virtual BinaryViewRef getData() = 0; @@ -228,8 +228,8 @@ class BINARYNINJAUIAPI View virtual LowLevelILFunctionRef getCurrentLowLevelILFunction() { return nullptr; } virtual MediumLevelILFunctionRef getCurrentMediumLevelILFunction() { return nullptr; } virtual HighLevelILFunctionRef getCurrentHighLevelILFunction() { return nullptr; } - virtual BNFunctionGraphType getILViewType() { return InvalidILViewType; } - virtual void setILViewType(BNFunctionGraphType ilViewType) {} + virtual BinaryNinja::FunctionViewType getILViewType() { return InvalidILViewType; } + virtual void setILViewType(const BinaryNinja::FunctionViewType& ilViewType) {} virtual size_t getCurrentILInstructionIndex() { return BN_INVALID_EXPR; } virtual size_t getSelectionStartILInstructionIndex() { return BN_INVALID_EXPR; } virtual BNILIndexRange getILIndexRange() { return {BN_INVALID_EXPR, BN_INVALID_EXPR}; } @@ -290,25 +290,25 @@ class BINARYNINJAUIAPI ViewLocation QString m_viewType; FunctionRef m_function = nullptr; uint64_t m_offset = 0; - BNFunctionGraphType m_ilViewType = InvalidILViewType; + BinaryNinja::FunctionViewType m_ilViewType = InvalidILViewType; size_t m_instrIndex = BN_INVALID_EXPR; public: ViewLocation() {} ViewLocation(const QString& viewType, uint64_t offset) : m_valid(true), m_viewType(viewType), m_offset(offset) {} - ViewLocation(const QString& viewType, uint64_t offset, BNFunctionGraphType ilViewType) : + ViewLocation(const QString& viewType, uint64_t offset, const BinaryNinja::FunctionViewType& ilViewType) : m_valid(true), m_viewType(viewType), m_offset(offset), m_ilViewType(ilViewType) {} - ViewLocation(const QString& viewType, uint64_t offset, BNFunctionGraphType ilViewType, size_t instrIndex) : + ViewLocation(const QString& viewType, uint64_t offset, const BinaryNinja::FunctionViewType& ilViewType, size_t instrIndex) : m_valid(true), m_viewType(viewType), m_offset(offset), m_ilViewType(ilViewType), m_instrIndex(instrIndex) {} - ViewLocation(const QString& viewType, FunctionRef function, uint64_t offset, BNFunctionGraphType ilViewType, + ViewLocation(const QString& viewType, FunctionRef function, uint64_t offset, const BinaryNinja::FunctionViewType& ilViewType, size_t instrIndex) : m_valid(true), m_viewType(viewType), m_function(function), m_offset(offset), m_ilViewType(ilViewType), m_instrIndex(instrIndex) {} ViewLocation( - FunctionRef function, uint64_t offset, BNFunctionGraphType ilViewType, size_t instrIndex = BN_INVALID_EXPR) : + FunctionRef function, uint64_t offset, const BinaryNinja::FunctionViewType& ilViewType, size_t instrIndex = BN_INVALID_EXPR) : m_valid(true), m_function(function), m_offset(offset), m_ilViewType(ilViewType), m_instrIndex(instrIndex) {} @@ -316,13 +316,13 @@ class BINARYNINJAUIAPI ViewLocation bool isValid() const { return m_valid; } QString getViewType() const { return m_viewType; } uint64_t getOffset() const { return m_offset; } - BNFunctionGraphType getILViewType() const { return m_ilViewType; } + const BinaryNinja::FunctionViewType& getILViewType() const { return m_ilViewType; } size_t getInstrIndex() const { return m_instrIndex; } FunctionRef getFunction() const { return m_function; } void setViewType(const QString& viewType) { m_viewType = viewType; } void setOffset(uint64_t offset) { m_offset = offset; } - void setILViewType(BNFunctionGraphType ilViewType) { m_ilViewType = ilViewType; } + void setILViewType(const BinaryNinja::FunctionViewType& ilViewType) { m_ilViewType = ilViewType; } void setInstrIndex(uint64_t index) { m_instrIndex = index; } void setFunction(FunctionRef function) { m_function = function; } diff --git a/ui/xreflist.h b/ui/xreflist.h index eb4edc7e..e9df2df8 100644 --- a/ui/xreflist.h +++ b/ui/xreflist.h @@ -304,7 +304,7 @@ class BINARYNINJAUIAPI CrossReferenceTreeModel : public QAbstractItemModel ViewFrame* m_view; std::vector<XrefItem> m_refs; size_t m_maxUIItems; - std::optional<BNFunctionGraphType> m_graphType; + std::optional<BinaryNinja::FunctionViewType> m_graphType; SelectionInfoForXref m_curRef; QTimer* m_updateTimer; @@ -328,7 +328,7 @@ class BINARYNINJAUIAPI CrossReferenceTreeModel : public QAbstractItemModel ViewFrame* getView() const { return m_view; } virtual void updateMaxUIItems(size_t value) { m_maxUIItems = value; } size_t getMaxUIItems() const { return m_maxUIItems; } - void setGraphType(BNFunctionGraphType type); + void setGraphType(const BinaryNinja::FunctionViewType& type); void requestAdvancedAnalysis(); Q_SIGNALS: @@ -352,7 +352,7 @@ class BINARYNINJAUIAPI CrossReferenceTableModel : public QAbstractTableModel ViewFrame* m_view; std::vector<XrefItem> m_refs; size_t m_maxUIItems; - std::optional<BNFunctionGraphType> m_graphType; + std::optional<BinaryNinja::FunctionViewType> m_graphType; SelectionInfoForXref m_curRef; QTimer* m_updateTimer; @@ -393,7 +393,7 @@ class BINARYNINJAUIAPI CrossReferenceTableModel : public QAbstractTableModel ViewFrame* getView() const { return m_view; } virtual void updateMaxUIItems(size_t value) { m_maxUIItems = value; } size_t getMaxUIItems() const { return m_maxUIItems; } - void setGraphType(BNFunctionGraphType type); + void setGraphType(const BinaryNinja::FunctionViewType& type); void requestAdvancedAnalysis(); Q_SIGNALS: void needRepaint(); @@ -530,7 +530,7 @@ public: virtual int filteredCount() const override; void updateTextFilter(const QString& filterText); virtual void updateMaxUIItems(size_t count) override; - void setGraphType(BNFunctionGraphType type) { m_tree->setGraphType(type); } + void setGraphType(const BinaryNinja::FunctionViewType& type) { m_tree->setGraphType(type); } virtual void OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override; Q_SIGNALS: @@ -574,7 +574,7 @@ class BINARYNINJAUIAPI CrossReferenceTable : public QTableView, public CrossRefe virtual int leafCount() const override; virtual int filteredCount() const override; virtual void updateMaxUIItems(size_t count) override; - void setGraphType(BNFunctionGraphType type) { m_table->setGraphType(type); } + void setGraphType(const BinaryNinja::FunctionViewType& type) { m_table->setGraphType(type); } virtual void OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override; public Q_SLOTS: @@ -609,7 +609,7 @@ class BINARYNINJAUIAPI CrossReferenceWidget : public SidebarWidget, public UICon CrossReferenceContainer* m_container; bool m_useTableView; Qt::Orientation m_primaryOrientation; - std::optional<BNFunctionGraphType> m_graphType; + std::optional<BinaryNinja::FunctionViewType> m_graphType; QTimer* m_hoverTimer; QPoint m_hoverPos; @@ -656,7 +656,7 @@ class BINARYNINJAUIAPI CrossReferenceWidget : public SidebarWidget, public UICon bool tableView() const { return m_useTableView; } bool uiMaxItemsExceeded() const { return m_uiMaxItemsExceeded; } void setUIMaxItemsExceeded(bool value) { m_uiMaxItemsExceeded = value; } - void setGraphType(BNFunctionGraphType type); + void setGraphType(const BinaryNinja::FunctionViewType& type); virtual void focus() override; |
