diff --git a/architecture.cpp b/architecture.cpp index f5fbf8925..4ed3ea509 100644 --- a/architecture.cpp +++ b/architecture.cpp @@ -24,6 +24,7 @@ #include #include #include "binaryninjaapi.h" +#include "lowlevelilinstruction.h" #include "ffi.h" using namespace BinaryNinja; @@ -307,6 +308,15 @@ bool Architecture::GetInstructionLowLevelILCallback( } +void Architecture::AnalyzeBasicBlocksCallback(void *ctxt, BNFunction* function, + BNBasicBlockAnalysisContext* context) +{ + CallbackRef arch(ctxt); + Ref func(new Function(BNNewFunctionReference(function))); + arch->AnalyzeBasicBlocks(*func, *context); +} + + char* Architecture::GetRegisterNameCallback(void* ctxt, uint32_t reg) { CallbackRef arch(ctxt); @@ -797,6 +807,7 @@ void Architecture::Register(Architecture* arch) callbacks.getInstructionText = GetInstructionTextCallback; callbacks.freeInstructionText = FreeInstructionTextCallback; callbacks.getInstructionLowLevelIL = GetInstructionLowLevelILCallback; + callbacks.analyzeBasicBlocks = AnalyzeBasicBlocksCallback; callbacks.getRegisterName = GetRegisterNameCallback; callbacks.getFlagName = GetFlagNameCallback; callbacks.getFlagWriteTypeName = GetFlagWriteTypeNameCallback; @@ -925,6 +936,12 @@ bool Architecture::GetInstructionLowLevelIL(const uint8_t*, uint64_t, size_t&, L } +void Architecture::AnalyzeBasicBlocks(Function& function, BasicBlockAnalysisContext& context) +{ + DefaultAnalyzeBasicBlocks(function, context); +} + + string Architecture::GetRegisterName(uint32_t reg) { return fmt::format("r{}", reg); @@ -1485,6 +1502,12 @@ bool CoreArchitecture::GetInstructionLowLevelIL(const uint8_t* data, uint64_t ad } +void CoreArchitecture::AnalyzeBasicBlocks(Function& function, BasicBlockAnalysisContext& context) +{ + BNArchitectureAnalyzeBasicBlocks(m_object, function.GetObject(), &context); +} + + string CoreArchitecture::GetRegisterName(uint32_t reg) { char* name = BNGetArchitectureRegisterName(m_object, reg); diff --git a/basicblock.cpp b/basicblock.cpp index 28ee393b0..e178bd6eb 100644 --- a/basicblock.cpp +++ b/basicblock.cpp @@ -317,6 +317,12 @@ uint64_t BasicBlock::GetStart() const } +void BasicBlock::SetEnd(uint64_t end) +{ + BNSetBasicBlockEnd(m_object, end); +} + + uint64_t BasicBlock::GetEnd() const { return BNGetBasicBlockEnd(m_object); @@ -385,6 +391,81 @@ bool BasicBlock::HasUndeterminedOutgoingEdges() const } +bool BasicBlock::HasInvalidInstructions() const +{ + return BNBasicBlockHasInvalidInstructions(m_object); +} + + +void BasicBlock::SetHasInvalidInstructions(bool value) +{ + BNBasicBlockSetHasInvalidInstructions(m_object, value); +} + + +void BasicBlock::AddPendingOutgoingEdge(BNBranchType type, uint64_t addr, Ref arch, bool fallThrough) +{ + BNBasicBlockAddPendingOutgoingEdge(m_object, type, addr, arch ? arch->GetObject() : nullptr, fallThrough); +} + + +vector BasicBlock::GetPendingOutgoingEdges() const +{ + size_t count; + BNPendingBasicBlockEdge* edges = BNGetBasicBlockPendingOutgoingEdges(m_object, &count); + vector result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + PendingBasicBlockEdge edge; + edge.type = edges[i].type; + edge.arch = edges[i].arch ? new CoreArchitecture(edges[i].arch) : nullptr; + edge.target = edges[i].target; + edge.fallThrough = edges[i].fallThrough; + result.push_back(edge); + } + + BNFreePendingBasicBlockEdgeList(edges); + return result; +} + + +void BasicBlock::ClearPendingOutgoingEdges() +{ + BNClearBasicBlockPendingOutgoingEdges(m_object); +} + + +void BasicBlock::SetUndeterminedOutgoingEdges(bool value) +{ + BNBasicBlockSetUndeterminedOutgoingEdges(m_object, value); +} + + +const uint8_t* BasicBlock::GetInstructionData(uint64_t addr, size_t* len) const +{ + return BNBasicBlockGetInstructionData(m_object, addr, len); +} + + +void BasicBlock::AddInstructionData(const void* data, size_t len) +{ + BNBasicBlockAddInstructionData(m_object, data, len); +} + + +void BasicBlock::SetFallThroughToFunction(bool value) +{ + BNBasicBlockSetFallThroughToFunction(m_object, value); +} + + +bool BasicBlock::IsFallThroughToFunction() const +{ + return BNBasicBlockIsFallThroughToFunction(m_object); +} + + bool BasicBlock::CanExit() const { return BNBasicBlockCanExit(m_object); diff --git a/binaryninjaapi.h b/binaryninjaapi.h index c911dc508..49ede7b5d 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -358,6 +358,10 @@ namespace BinaryNinja { bool operator<(const Ref& obj) const { return T::GetObject(m_obj) < T::GetObject(obj.m_obj); } + bool operator>(const T* obj) const { return T::GetObject(m_obj) > T::GetObject(obj); } + + bool operator>(const Ref& obj) const { return T::GetObject(m_obj) > T::GetObject(obj.m_obj); } + T* GetPtr() const { return m_obj; } }; @@ -578,6 +582,7 @@ namespace BinaryNinja { class FlowGraph; class ReportCollection; struct FormInputField; + struct ArchAndAddr; /*! Logs to the error console with the given BNLogLevel. @@ -5299,6 +5304,13 @@ namespace BinaryNinja { */ void AbortAnalysis(); + + /*! Check whether analysis is currently running + + \return true if analysis is aborted, false otherwise + */ + bool AnalysisIsAborted() const; + /*! Define a DataVariable at a given address with a set type \param addr virtual address to define the DataVariable at @@ -6895,6 +6907,17 @@ namespace BinaryNinja { bool GetNewAutoFunctionAnalysisSuppressed(); void SetNewAutoFunctionAnalysisSuppressed(bool suppress); + /*! Determine whether the target analysis should be skipped for a given source function and target address + + \param source Source function and address + \param sourceFunc Function at the source address + \param sourceEnd End address of the source function + \param target Target address to analyze + \return Whether the target analysis should be skipped + */ + bool ShouldSkipTargetAnalysis(const ArchAndAddr& source, Ref sourceFunc, + uint64_t sourceEnd, const ArchAndAddr& target); + /*! Returns a list of namespaces for the current BinaryView \return A list of namespaces for the current BinaryView @@ -8041,6 +8064,7 @@ namespace BinaryNinja { class RelocationHandler; typedef size_t ExprId; + typedef BNBasicBlockAnalysisContext BasicBlockAnalysisContext; /*! The Architecture class is the base class for all CPU architectures. This provides disassembly, assembly, patching, and IL translation lifting for a given architecture. @@ -8069,6 +8093,7 @@ namespace BinaryNinja { static void FreeInstructionTextCallback(BNInstructionTextToken* tokens, size_t count); static bool GetInstructionLowLevelILCallback( void* ctxt, const uint8_t* data, uint64_t addr, size_t* len, BNLowLevelILFunction* il); + static void AnalyzeBasicBlocksCallback(void *ctxt, BNFunction* function, BNBasicBlockAnalysisContext* context); static char* GetRegisterNameCallback(void* ctxt, uint32_t reg); static char* GetFlagNameCallback(void* ctxt, uint32_t flag); static char* GetFlagWriteTypeNameCallback(void* ctxt, uint32_t flags); @@ -8141,6 +8166,15 @@ namespace BinaryNinja { */ static void Register(Architecture* arch); + static void DefaultAnalyzeBasicBlocksCallback(BNFunction* function, BNBasicBlockAnalysisContext* context); + + /*! Default implementation of AnalyzeBasicBlocks + + \param function Function to analyze + \param context Context for the analysis + */ + static void DefaultAnalyzeBasicBlocks(Function& function, BasicBlockAnalysisContext& context); + /*! Get an Architecture by name \param name Name of the architecture @@ -8239,6 +8273,13 @@ namespace BinaryNinja { */ virtual bool GetInstructionLowLevelIL(const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il); + /*! Analyze the basic blocks of a function + + \param function Function to analyze + \param context Context for the analysis + */ + virtual void AnalyzeBasicBlocks(Function& function, BasicBlockAnalysisContext& context); + /*! Gets a register name from a register index. \param reg Register index @@ -8517,7 +8558,6 @@ namespace BinaryNinja { \return Whether the conversion was successful */ virtual bool SkipAndReturnValue(uint8_t* data, uint64_t addr, size_t len, uint64_t value); - void RegisterFunctionRecognizer(FunctionRecognizer* recog); void RegisterRelocationHandler(const std::string& viewName, RelocationHandler* handler); Ref GetRelocationHandler(const std::string& viewName); @@ -8633,6 +8673,7 @@ namespace BinaryNinja { const uint8_t* data, uint64_t addr, size_t& len, std::vector& result) override; virtual bool GetInstructionLowLevelIL( const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il) override; + virtual void AnalyzeBasicBlocks(Function& function, BasicBlockAnalysisContext& context) override; virtual std::string GetRegisterName(uint32_t reg) override; virtual std::string GetFlagName(uint32_t flag) override; virtual std::string GetFlagWriteTypeName(uint32_t flags) override; @@ -10465,6 +10506,17 @@ namespace BinaryNinja { bool fallThrough; }; + /*! + \ingroup basicblocks + */ + struct PendingBasicBlockEdge + { + BNBranchType type; + Ref arch; + uint64_t target; + bool fallThrough; + }; + /*! \ingroup basicblocks */ @@ -10491,6 +10543,13 @@ namespace BinaryNinja { */ uint64_t GetStart() const; + + /*! Set the end of a basic block + + \param end Ending address of the basic block + */ + void SetEnd(uint64_t end); + /*! Ending address of the basic block \return Ending address of the basic block @@ -10527,6 +10586,72 @@ namespace BinaryNinja { */ bool HasUndeterminedOutgoingEdges() const; + + /*! Whether the basic block has invalid instructions + + \return true if the basic block has invalid instructions, false otherwise + */ + bool HasInvalidInstructions() const; + + /*! Set whether the basic block has invalid instructions + + \param value true if the basic block has invalid instructions, false otherwise + */ + void SetHasInvalidInstructions(bool value); + + /*! Add a pending outgoing edge to this basic block + + \param type Type of the branch + \param addr Address of the target basic block + \param arch Optional architecture for the target basic block, default is nullptr + \param fallThrough Whether this is a fall-through edge, default false + */ + void AddPendingOutgoingEdge(BNBranchType type, uint64_t addr, Ref arch = nullptr, + bool fallThrough = false); + + /*! Get a list of pending outgoing edges for this basic block + + \return List of pending outgoing edges + */ + std::vector GetPendingOutgoingEdges() const; + + /*! Clear the pending outgoing edges for this basic block + */ + void ClearPendingOutgoingEdges(); + + /*! Set whether basic block has undetermined outgoing edges + + \param value Whether basic block has undetermined outgoing edges + */ + void SetUndeterminedOutgoingEdges(bool value); + + /*! Get the instruction data for a specific address in this basic block + + \param addr Address of the instruction + \param len Pointer to a size_t variable to store the length of the instruction data + \return Pointer to the instruction data + */ + const uint8_t* GetInstructionData(uint64_t addr, size_t* len) const; + + /*! Add instruction data to the basic block + + \param data Pointer to the instruction data + \param len Length of the instruction data + */ + void AddInstructionData(const void* data, size_t len); + + /*! Set whether the basic blocks falls through to a function + + \param value Whether the basic block falls through to a function + */ + void SetFallThroughToFunction(bool value); + + /*! Determine whether the basic block falls through to a function + + \return Whether basic block falls through to a function + */ + bool IsFallThroughToFunction() const; + /*! Whether basic block can return or is tagged as 'No Return' \return Whether basic block can return or is tagged as 'No Return' @@ -10773,7 +10898,9 @@ namespace BinaryNinja { address = a.address; return *this; } - bool operator==(const ArchAndAddr& a) const { return (arch == a.arch) && (address == a.address); } + bool operator==(const ArchAndAddr& a) const { + return (arch == a.arch) && (address == a.address); + } bool operator<(const ArchAndAddr& a) const { if (arch < a.arch) @@ -10918,6 +11045,24 @@ namespace BinaryNinja { */ std::vector> GetBasicBlocks() const; + /*! Create a new basic block for this function + + \param arch Architecture for the basic block + \param addr Address of the basic block + \return The new BasicBlock + */ + Ref CreateBasicBlock(Architecture* arch, uint64_t addr); + + /*! Add a basic block to the function analysis basic block list + + \param block The BasicBlock to add + */ + void AddBasicBlock(Ref block); + + /*! Finalize basic block list for this function + */ + void FinalizeBasicBlocks(); + /*! Get the basic block an address is located in \param arch Architecture for the basic block @@ -11247,6 +11392,17 @@ namespace BinaryNinja { std::vector GetIndirectBranches(); std::vector GetIndirectBranchesAt(Architecture* arch, uint64_t addr); + void AddDirectCodeReference(const ArchAndAddr& source, uint64_t target); + void AddDirectNoReturnCall(const ArchAndAddr& location); + bool LocationHasNoReturnCalls(const ArchAndAddr& location) const; + Ref GetCalleeForAnalysis(Ref platform, uint64_t addr, bool exact); + void AddTempOutgoingReference(Ref target); + bool HasTempOutgoingReference(Ref target) const; + void AddTempIncomingReference(Ref source); + + bool GetContextualFunctionReturn(const ArchAndAddr& location, bool& value) const; + void SetContextualFunctionReturn(const ArchAndAddr& location, bool value); + std::vector GetUnresolvedIndirectBranches(); bool HasUnresolvedIndirectBranches(); diff --git a/binaryninjacore.h b/binaryninjacore.h index 91e366f4f..b5861eb11 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,7 +37,7 @@ // 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 105 +#define BN_CURRENT_CORE_ABI_VERSION 106 // 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 @@ -307,6 +307,7 @@ extern "C" typedef struct BNLineFormatter BNLineFormatter; typedef struct BNRenderLayer BNRenderLayer; typedef struct BNStringRef BNStringRef; + typedef struct BNIndirectBranchInfo BNIndirectBranchInfo; typedef bool(*BNProgressFunction)(void*, size_t, size_t); @@ -1844,6 +1845,23 @@ extern "C" uint8_t confidence; } BNTypeWithConfidence; + typedef enum BNFunctionAnalysisSkipOverride + { + DefaultFunctionAnalysisSkip, + NeverSkipFunctionAnalysis, + AlwaysSkipFunctionAnalysis + } BNFunctionAnalysisSkipOverride; + + typedef struct BNBasicBlockAnalysisContext + { + size_t indirectBranchesCount; + BNIndirectBranchInfo* indirectBranches; + BNFunctionAnalysisSkipOverride analysisSkipOverride; + bool translateTailCalls; + bool disallowBranchToString; + uint64_t maxFunctionSize; + } BNBasicBlockAnalysisContext; + typedef struct BNCustomArchitecture { void* context; @@ -1862,6 +1880,7 @@ extern "C" void (*freeInstructionText)(BNInstructionTextToken* tokens, size_t count); bool (*getInstructionLowLevelIL)( void* ctxt, const uint8_t* data, uint64_t addr, size_t* len, BNLowLevelILFunction* il); + void (*analyzeBasicBlocks)(void* ctxt, BNFunction* function, BNBasicBlockAnalysisContext* context); char* (*getRegisterName)(void* ctxt, uint32_t reg); char* (*getFlagName)(void* ctxt, uint32_t flag); char* (*getFlagWriteTypeName)(void* ctxt, uint32_t flags); @@ -1966,6 +1985,14 @@ extern "C" bool fallThrough; } BNBasicBlockEdge; + typedef struct BNPendingBasicBlockEdge + { + BNBranchType type; + BNArchitecture* arch; + uint64_t target; + bool fallThrough; + } BNPendingBasicBlockEdge; + typedef struct BNPoint { float x; @@ -3130,13 +3157,6 @@ extern "C" uint8_t confidence; } BNRegisterStackAdjustment; - typedef enum BNFunctionAnalysisSkipOverride - { - DefaultFunctionAnalysisSkip, - NeverSkipFunctionAnalysis, - AlwaysSkipFunctionAnalysis - } BNFunctionAnalysisSkipOverride; - typedef enum BNReportType { PlainTextReportType, @@ -4461,6 +4481,10 @@ extern "C" BINARYNINJACOREAPI bool BNGetInstructionLowLevelIL( BNArchitecture* arch, const uint8_t* data, uint64_t addr, size_t* len, BNLowLevelILFunction* il); BINARYNINJACOREAPI void BNFreeInstructionText(BNInstructionTextToken* tokens, size_t count); + BINARYNINJACOREAPI bool BNArchitectureSetDefaultAnalyzeBasicBlocksCallback(void *callback); + BINARYNINJACOREAPI void BNArchitectureDefaultAnalyzeBasicBlocks(BNFunction* function, BNBasicBlockAnalysisContext* context); + BINARYNINJACOREAPI void BNArchitectureAnalyzeBasicBlocks(BNArchitecture* arch, BNFunction* function, + BNBasicBlockAnalysisContext* context); BINARYNINJACOREAPI void BNFreeInstructionTextLines(BNInstructionTextLine* lines, size_t count); BINARYNINJACOREAPI char* BNGetArchitectureRegisterName(BNArchitecture* arch, uint32_t reg); BINARYNINJACOREAPI char* BNGetArchitectureFlagName(BNArchitecture* arch, uint32_t flag); @@ -4576,6 +4600,7 @@ extern "C" BINARYNINJACOREAPI void BNUpdateAnalysisAndWait(BNBinaryView* view); BINARYNINJACOREAPI void BNUpdateAnalysis(BNBinaryView* view); BINARYNINJACOREAPI void BNAbortAnalysis(BNBinaryView* view); + BINARYNINJACOREAPI bool BNAnalysisIsAborted(BNBinaryView* view); BINARYNINJACOREAPI bool BNIsFunctionUpdateNeeded(BNFunction* func); BINARYNINJACOREAPI void BNRequestAdvancedFunctionAnalysisData(BNFunction* func); BINARYNINJACOREAPI void BNReleaseAdvancedFunctionAnalysisData(BNFunction* func); @@ -4639,6 +4664,9 @@ extern "C" BINARYNINJACOREAPI void BNFreeBasicBlock(BNBasicBlock* block); BINARYNINJACOREAPI BNBasicBlock** BNGetFunctionBasicBlockList(BNFunction* func, size_t* count); BINARYNINJACOREAPI void BNFreeBasicBlockList(BNBasicBlock** blocks, size_t count); + BINARYNINJACOREAPI BNBasicBlock* BNCreateFunctionBasicBlock(BNFunction* func, BNArchitecture* arch, uint64_t addr); + BINARYNINJACOREAPI void BNAddFunctionBasicBlock(BNFunction* func, BNBasicBlock* block); + BINARYNINJACOREAPI void BNFinalizeFunctionBasicBlocks(BNFunction* func); BINARYNINJACOREAPI BNBasicBlock* BNGetFunctionBasicBlockAtAddress( BNFunction* func, BNArchitecture* arch, uint64_t addr); BINARYNINJACOREAPI BNBasicBlock* BNGetRecentBasicBlockForAddress(BNBinaryView* view, uint64_t addr); @@ -4776,14 +4804,26 @@ extern "C" BINARYNINJACOREAPI BNArchitecture* BNGetBasicBlockArchitecture(BNBasicBlock* block); BINARYNINJACOREAPI BNBasicBlock* BNGetBasicBlockSource(BNBasicBlock* block); BINARYNINJACOREAPI uint64_t BNGetBasicBlockStart(BNBasicBlock* block); + BINARYNINJACOREAPI void BNSetBasicBlockEnd(BNBasicBlock* block, uint64_t end); BINARYNINJACOREAPI uint64_t BNGetBasicBlockEnd(BNBasicBlock* block); BINARYNINJACOREAPI uint64_t BNGetBasicBlockLength(BNBasicBlock* block); BINARYNINJACOREAPI BNBasicBlockEdge* BNGetBasicBlockOutgoingEdges(BNBasicBlock* block, size_t* count); BINARYNINJACOREAPI BNBasicBlockEdge* BNGetBasicBlockIncomingEdges(BNBasicBlock* block, size_t* count); BINARYNINJACOREAPI void BNFreeBasicBlockEdgeList(BNBasicBlockEdge* edges, size_t count); BINARYNINJACOREAPI bool BNBasicBlockHasUndeterminedOutgoingEdges(BNBasicBlock* block); + BINARYNINJACOREAPI void BNBasicBlockAddPendingOutgoingEdge(BNBasicBlock* block, BNBranchType type, + uint64_t addr, BNArchitecture* arch, bool fallThrough); + BINARYNINJACOREAPI BNPendingBasicBlockEdge* BNGetBasicBlockPendingOutgoingEdges(BNBasicBlock* block, size_t* count); + BINARYNINJACOREAPI void BNFreePendingBasicBlockEdgeList(BNPendingBasicBlockEdge* edges); + BINARYNINJACOREAPI void BNClearBasicBlockPendingOutgoingEdges(BNBasicBlock* block); + BINARYNINJACOREAPI void BNBasicBlockSetUndeterminedOutgoingEdges(BNBasicBlock* block, bool value); + BINARYNINJACOREAPI const uint8_t* BNBasicBlockGetInstructionData(BNBasicBlock* block, uint64_t addr, size_t* len); + BINARYNINJACOREAPI void BNBasicBlockAddInstructionData(BNBasicBlock* block, const void* data, size_t len); + BINARYNINJACOREAPI void BNBasicBlockSetFallThroughToFunction(BNBasicBlock* block, bool value); + BINARYNINJACOREAPI bool BNBasicBlockIsFallThroughToFunction(BNBasicBlock* block); BINARYNINJACOREAPI bool BNBasicBlockCanExit(BNBasicBlock* block); BINARYNINJACOREAPI void BNBasicBlockSetCanExit(BNBasicBlock* block, bool value); + BINARYNINJACOREAPI void BNBasicBlockSetHasInvalidInstructions(BNBasicBlock* block, bool value); BINARYNINJACOREAPI bool BNBasicBlockHasInvalidInstructions(BNBasicBlock* block); BINARYNINJACOREAPI size_t BNGetBasicBlockIndex(BNBasicBlock* block); BINARYNINJACOREAPI BNBasicBlock** BNGetBasicBlockDominators(BNBasicBlock* block, size_t* count, bool post); @@ -5000,6 +5040,18 @@ extern "C" BINARYNINJACOREAPI BNIndirectBranchInfo* BNGetIndirectBranchesAt( BNFunction* func, BNArchitecture* arch, uint64_t addr, size_t* count); BINARYNINJACOREAPI void BNFreeIndirectBranchList(BNIndirectBranchInfo* branches); + BINARYNINJACOREAPI void BNFunctionAddDirectCodeReference(BNFunction* func, BNArchitectureAndAddress* source, + uint64_t target); + BINARYNINJACOREAPI void BNFunctionAddDirectNoReturnCall(BNFunction* func, BNArchitectureAndAddress* location); + BINARYNINJACOREAPI bool BNFunctionLocationHasNoReturnCalls(BNFunction* func, BNArchitectureAndAddress* location); + BINARYNINJACOREAPI BNFunction* BNGetCalleeForAnalysis(BNFunction* func, BNPlatform* platform, + uint64_t addr, bool exact); + BINARYNINJACOREAPI void BNFunctionAddTempOutgoingReference(BNFunction* func, BNFunction* target); + BINARYNINJACOREAPI bool BNFunctionHasTempOutgoingReference(BNFunction* func, BNFunction* target); + BINARYNINJACOREAPI void BNFunctionAddTempIncomingReference(BNFunction* func, BNFunction* source); + + BINARYNINJACOREAPI bool BNFunctionGetContextualFunctionReturn(BNFunction* func, BNArchitectureAndAddress* location, bool* value); + BINARYNINJACOREAPI void BNFunctionSetContextualFunctionReturn(BNFunction* func, BNArchitectureAndAddress* location, bool value); BINARYNINJACOREAPI uint64_t* BNGetUnresolvedIndirectBranches(BNFunction* func, size_t* count); BINARYNINJACOREAPI bool BNHasUnresolvedIndirectBranches(BNFunction* func); @@ -5062,13 +5114,14 @@ extern "C" BINARYNINJACOREAPI void BNSetMaxFunctionSizeForAnalysis(BNBinaryView* view, uint64_t size); BINARYNINJACOREAPI bool BNGetNewAutoFunctionAnalysisSuppressed(BNBinaryView* view); BINARYNINJACOREAPI void BNSetNewAutoFunctionAnalysisSuppressed(BNBinaryView* view, bool suppress); - BINARYNINJACOREAPI BNAnalysisCompletionEvent* BNAddAnalysisCompletionEvent( BNBinaryView* view, void* ctxt, void (*callback)(void* ctxt)); BINARYNINJACOREAPI BNAnalysisCompletionEvent* BNNewAnalysisCompletionEventReference( BNAnalysisCompletionEvent* event); BINARYNINJACOREAPI void BNFreeAnalysisCompletionEvent(BNAnalysisCompletionEvent* event); BINARYNINJACOREAPI void BNCancelAnalysisCompletionEvent(BNAnalysisCompletionEvent* event); + BINARYNINJACOREAPI bool BNShouldSkipTargetAnalysis(BNBinaryView* view, BNArchitectureAndAddress* source, + BNFunction* sourceFunc, uint64_t sourceEnd, BNArchitectureAndAddress* target); BINARYNINJACOREAPI BNAnalysisInfo* BNGetAnalysisInfo(BNBinaryView* view); BINARYNINJACOREAPI void BNFreeAnalysisInfo(BNAnalysisInfo* info); diff --git a/binaryview.cpp b/binaryview.cpp index 242b2a010..18a76596d 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -2174,6 +2174,12 @@ void BinaryView::AbortAnalysis() } +bool BinaryView::AnalysisIsAborted() const +{ + return BNAnalysisIsAborted(m_object); +} + + void BinaryView::DefineDataVariable(uint64_t addr, const Confidence>& type) { BNTypeWithConfidence tc; @@ -5255,6 +5261,20 @@ void BinaryView::SetNewAutoFunctionAnalysisSuppressed(bool suppress) } +bool BinaryView::ShouldSkipTargetAnalysis(const ArchAndAddr& source, Ref sourceFunc, + uint64_t sourceEnd, const ArchAndAddr& target) +{ + BNArchitectureAndAddress sourceCopy; + sourceCopy.arch = source.arch ? source.arch->GetObject() : nullptr; + sourceCopy.address = source.address; + BNArchitectureAndAddress targetCopy; + targetCopy.address = target.address; + targetCopy.arch = target.arch ? target.arch->GetObject() : nullptr; + auto func = sourceFunc ? sourceFunc->GetObject() : nullptr; + return BNShouldSkipTargetAnalysis(m_object, &sourceCopy, func, sourceEnd, &targetCopy); +} + + set BinaryView::GetNameSpaces() const { set nameSpaces; diff --git a/defaultabb.cpp b/defaultabb.cpp new file mode 100644 index 000000000..b69e0c2f8 --- /dev/null +++ b/defaultabb.cpp @@ -0,0 +1,648 @@ +#include +#include +#include +#include +#include "binaryninjaapi.h" +#include "binaryninjacore.h" +#include "lowlevelilinstruction.h" + +using namespace std; +using namespace BinaryNinja; + +// TODO: Decomposed from BinaryView::IsOffsetCodeSemantics BinaryView::IsOffsetExternSemantics +// TODO: When the better sections model is merged, remove this +static bool IsOffsetCodeSemanticsFast(BinaryView* data, const vector& readOnlySections, const vector& dataExternSections, uint64_t offset) +{ + if (!data->IsOffsetBackedByFile(offset)) + return false; + + for (const auto& i : readOnlySections) + { + if ((offset >= i->GetStart()) && (offset < i->GetEnd())) + return true; + } + for (const auto& i : dataExternSections) + { + if ((offset >= i->GetStart()) && (offset < i->GetEnd())) + return false; + } + + return data->IsOffsetExecutable(offset); +} + + +static bool IsOffsetExternSemanticsFast(BinaryView* data, const vector& externSections, uint64_t offset) +{ + if (data->IsOffsetBackedByFile(offset)) + return false; + if (data->IsOffsetExecutable(offset)) + return false; + + for (const auto& i : externSections) + { + if ((offset >= i->GetStart()) && (offset < i->GetEnd())) + return true; + } + + return false; +} + + +static bool GetNextFunctionAfterAddress(Ref data, Ref platform, uint64_t address, Ref& nextFunc) +{ + uint64_t nextFuncAddr = data->GetNextFunctionStartAfterAddress(address); + nextFunc = data->GetAnalysisFunction(platform, nextFuncAddr); + return nextFunc != nullptr; +} + + +void Architecture::DefaultAnalyzeBasicBlocks(Function& function, BasicBlockAnalysisContext& context) +{ + auto data = function.GetView(); + queue blocksToProcess; + map> instrBlocks; + set seenBlocks; + map> indirectBranches; + for (size_t i = 0; i < context.indirectBranchesCount; i++) + { + auto sourceLocation = ArchAndAddr(new CoreArchitecture(context.indirectBranches[i].sourceArch), + context.indirectBranches[i].sourceAddr); + auto destLocation = ArchAndAddr(new CoreArchitecture(context.indirectBranches[i].destArch), + context.indirectBranches[i].destAddr); + indirectBranches[sourceLocation].insert(destLocation); + } + + BNStringReference strRef; + auto targetExceedsByteLimit = [](const BNStringReference& strRef) { + size_t byteLimit = 8; + if (strRef.type == Utf16String) byteLimit *= 2; + else if (strRef.type == Utf32String) byteLimit *= 4; + return (strRef.length >= byteLimit); + }; + + // TODO: Decomposed from BinaryView::IsOffsetCodeSemantics BinaryView::IsOffsetExternSemantics + // TODO: When the better sections model is merged, remove this + auto sections = data->GetSections(); + vector externSections, readOnlySections, dataExternSections; + externSections.reserve(sections.size()); + readOnlySections.reserve(sections.size()); + dataExternSections.reserve(sections.size()); + for (auto& section: sections) + { + if (section->GetSemantics() == ExternalSectionSemantics) + { + externSections.push_back(section); + } + if (section->GetSemantics() == ReadOnlyCodeSectionSemantics) + { + readOnlySections.push_back(section); + } + if ((section->GetSemantics() == ReadOnlyDataSectionSemantics) || + (section->GetSemantics() == ReadWriteDataSectionSemantics) || + (section->GetSemantics() == ExternalSectionSemantics)) + { + dataExternSections.push_back(section); + } + } + + // Start by processing the entry point of the function + auto funcPlatform = function.GetPlatform(); + auto start = function.GetStart(); + blocksToProcess.emplace(funcPlatform->GetArchitecture(), start); + seenBlocks.emplace(funcPlatform->GetArchitecture(), start); + + // Only validate that branch destinations are executable if the start of the function is executable. This allows + // data to be disassembled manually + bool validateExecutable = data->IsOffsetExecutable(start); + + bool fastValidate = false; + uint64_t fastEndAddr = 0; + uint64_t fastStartAddr = UINT64_MAX; + if (validateExecutable) + { + // Extract the bounds of the section containing this + // function, to avoid calling into the BinaryView on + // every instruction. + for (auto& sec : data->GetSectionsAt(start)) + { + if (sec->GetSemantics() == ReadOnlyDataSectionSemantics) + continue; + if (sec->GetSemantics() == ReadWriteDataSectionSemantics) + continue; + if (!data->IsOffsetBackedByFile(sec->GetStart())) + continue; + if (!data->IsOffsetExecutable(sec->GetStart())) + continue; + if (fastStartAddr > sec->GetStart()) + fastStartAddr = sec->GetStart(); + if (fastEndAddr < (sec->GetEnd() - 1)) + { + fastEndAddr = sec->GetEnd() - 1; + Ref segment = data->GetSegmentAt(fastEndAddr); + if (segment) + fastEndAddr = (std::min)(fastEndAddr, segment->GetDataEnd() - 1); + } + fastValidate = true; + break; + } + } + + uint64_t totalSize = 0; + uint64_t maxSize = context.maxFunctionSize; + while (blocksToProcess.size() != 0) + { + if (data->AnalysisIsAborted()) + return; + + // Get the next block to process + ArchAndAddr location = blocksToProcess.front(); + ArchAndAddr instructionGroupStart = location; + blocksToProcess.pop(); + + // Create a new basic block + Ref block = function.CreateBasicBlock(location.arch, location.address); + + // Get the next function to prevent disassembling into the next function if the block falls through + Ref nextFunc; + bool hasNextFunc = GetNextFunctionAfterAddress(data, funcPlatform, location.address, nextFunc); + uint64_t nextFuncAddr = (hasNextFunc && nextFunc) ? nextFunc->GetStart() : 0; + set> calledFunctions; + + // we mostly only case if this is 0, or more than 0. after handling an instruction, + // we decrement. the architecture can change this value arbitrarily during callbacks. + uint8_t delaySlotCount = 0; + bool delayInstructionEndsBlock = false; + + // Disassemble the instructions in the block + while (true) + { + if (data->AnalysisIsAborted()) + return; + + if (!delaySlotCount) + { + auto blockIter = instrBlocks.find(location); + if (blockIter != instrBlocks.end()) + { + // This instruction has already been seen, go to it directly insread of creating a copy + Ref targetBlock = blockIter->second; + if (targetBlock->GetStart() == location.address) + { + // Instruction is the start of a block, add an unconditional branch to it + block->AddPendingOutgoingEdge(UnconditionalBranch, location.address, nullptr, + (block->GetStart() != location.address)); + break; + } + else + { + // Instruction is in the middle of a block, need to split the basic block into two + Ref splitBlock = function.CreateBasicBlock(location.arch, location.address); + size_t instrDataLen; + const uint8_t* instrData = targetBlock->GetInstructionData(location.address, &instrDataLen); + splitBlock->AddInstructionData(instrData, instrDataLen); + splitBlock->SetFallThroughToFunction(targetBlock->IsFallThroughToFunction()); + splitBlock->SetUndeterminedOutgoingEdges(targetBlock->HasUndeterminedOutgoingEdges()); + splitBlock->SetCanExit(targetBlock->CanExit()); + splitBlock->SetEnd(targetBlock->GetEnd()); + + targetBlock->SetFallThroughToFunction(false); + targetBlock->SetUndeterminedOutgoingEdges(false); + targetBlock->SetCanExit(true); + targetBlock->SetEnd(location.address); + + // Place instructions after the split point into the new block + for (size_t j = location.address; j < splitBlock->GetEnd(); j++) + { + auto k = instrBlocks.find(ArchAndAddr(location.arch, j)); + if ((k != instrBlocks.end()) && (k->second == targetBlock)) + k->second = splitBlock; + } + + for (auto& k : targetBlock->GetPendingOutgoingEdges()) + splitBlock->AddPendingOutgoingEdge(k.type, k.target, k.arch, k.fallThrough); + targetBlock->ClearPendingOutgoingEdges(); + targetBlock->AddPendingOutgoingEdge(UnconditionalBranch, location.address, nullptr, true); + + // Mark the new block so that it will not be processed again + seenBlocks.insert(location); + function.AddBasicBlock(splitBlock); + + // Add an outgoing edge from the current block to the new block + block->AddPendingOutgoingEdge(UnconditionalBranch, location.address); + break; + } + } + } + + uint8_t opcode[BN_MAX_INSTRUCTION_LENGTH]; + size_t maxLen = data->Read(opcode, location.address, location.arch->GetMaxInstructionLength()); + if (maxLen == 0) + { + string text = fmt::format("Could not read instruction at {:#x}", location.address); + function.CreateAutoAddressTag(location.arch, location.address, "Invalid Instruction", text, true); + if (location.arch->GetInstructionAlignment() == 0) + location.address++; + else + location.address += location.arch->GetInstructionAlignment(); + block->SetHasInvalidInstructions(true); + break; + } + + InstructionInfo info; + info.delaySlots = delaySlotCount; + if (!location.arch->GetInstructionInfo(opcode, location.address, maxLen, info)) + { + string text = fmt::format("Could not get instruction info at {:#x}", location.address); + function.CreateAutoAddressTag(location.arch, location.address, "Invalid Instruction", text, true); + if (location.arch->GetInstructionAlignment() == 0) + location.address++; + else + location.address += location.arch->GetInstructionAlignment(); + block->SetHasInvalidInstructions(true); + break; + } + + // The instruction is invalid if it has no length or is above maximum length + if ((info.length == 0) || (info.length > maxLen)) + { + string text = fmt::format("Instruction of invalid length at {:#x}", location.address); + function.CreateAutoAddressTag(location.arch, location.address, "Invalid Instruction", text, true); + if (location.arch->GetInstructionAlignment() == 0) + location.address++; + else + location.address += location.arch->GetInstructionAlignment(); + block->SetHasInvalidInstructions(true); + break; + } + + // Instruction is invalid when straddling a boundary to a section that is non-code, or not back by file + uint64_t instrEnd = location.address + info.length - 1; + bool slowPath = !fastValidate || (instrEnd < fastStartAddr) || (instrEnd > fastEndAddr); + if (slowPath && + ((!IsOffsetCodeSemanticsFast(data, readOnlySections, dataExternSections, instrEnd) && IsOffsetCodeSemanticsFast(data, readOnlySections, dataExternSections,location.address)) || + (!data->IsOffsetBackedByFile(instrEnd) && data->IsOffsetBackedByFile(location.address)))) + { + string text = fmt::format("Instruction at {:#x} straddles a non-code section", location.address); + function.CreateAutoAddressTag(location.arch, location.address, "Invalid Instruction", text, true); + if (location.arch->GetInstructionAlignment() == 0) + location.address++; + else + location.address += location.arch->GetInstructionAlignment(); + block->SetHasInvalidInstructions(true); + break; + } + + bool endsBlock = false; + ArchAndAddr target; + map>::const_iterator indirectBranchIter, endIter; + if (!delaySlotCount) + { + // Register the address as belonging to this block if not in a delay slot, + // this prevents basic blocks from being split between an instruction and + // any of its delay slots + instrBlocks[location] = block; + + // Keep track of where the current 'group' of instructions started. A 'group' + // is an instruction and all of its delay slot instructions. + instructionGroupStart = location; + + // Don't process branches in delay slots + for (size_t i = 0; i < info.branchCount; i++) + { + bool fastPath; + + switch (info.branchType[i]) + { + case UnconditionalBranch: + case TrueBranch: + case FalseBranch: + // Normal branch, resume disassembly at targets + endsBlock = true; + // Target of a call instruction, add the function to the analysis + if (IsOffsetExternSemanticsFast(data, externSections, info.branchTarget[i])) + { + // Deal with direct pointers into the extern section + DataVariable dataVar; + if (data->GetDataVariableAtAddress(info.branchTarget[i], dataVar) && + (dataVar.address == info.branchTarget[i]) && dataVar.type && + (dataVar.type->GetClass() == FunctionTypeClass)) + { + function.AddDirectCodeReference(location, info.branchTarget[i]); + if (!dataVar.type->CanReturn()) + { + function.AddDirectNoReturnCall(location); + endsBlock = true; + block->SetCanExit(false); + } + } + break; + } + + fastPath = fastValidate && (info.branchTarget[i] >= fastStartAddr) && (info.branchTarget[i] <= fastEndAddr); + if (fastPath || (data->IsValidOffset(info.branchTarget[i]) && + data->IsOffsetBackedByFile(info.branchTarget[i]) && + ((!validateExecutable) || data->IsOffsetExecutable(info.branchTarget[i])))) + { + target = ArchAndAddr(info.branchArch[i] ? new CoreArchitecture(info.branchArch[i]) : location.arch, info.branchTarget[i]); + + // Check if valid target + if (data->ShouldSkipTargetAnalysis(location, &function, instrEnd, target)) + break; + + Platform* targetPlatform = funcPlatform; + if (target.arch != funcPlatform->GetArchitecture()) + targetPlatform = funcPlatform->GetRelatedPlatform(target.arch); + + function.AddDirectCodeReference(location, info.branchTarget[i]); + + auto otherFunc = function.GetCalleeForAnalysis(targetPlatform, target.address, true); + if (context.translateTailCalls && targetPlatform && otherFunc && (otherFunc->GetStart() != function.GetStart())) + { + calledFunctions.insert(otherFunc); + if (info.branchType[i] == UnconditionalBranch) + { + if (!otherFunc->CanReturn()) + { + function.AddDirectNoReturnCall(location); + endsBlock = true; + block->SetCanExit(false); + } + + break; + } + } + else if (context.disallowBranchToString && data->GetStringAtAddress(location.address, strRef) && targetExceedsByteLimit(strRef)) + { + BNLogInfo("Not adding branch target from 0x%" PRIx64 " to string at 0x%" PRIx64 + " length:%zu", + location.address, target.address, strRef.length); + break; + } + else + { + + block->AddPendingOutgoingEdge(info.branchType[i], target.address, target.arch); + // Add the block to the list of blocks to process if it is not already processed + if (seenBlocks.count(target) == 0) + { + blocksToProcess.push(target); + seenBlocks.insert(target); + } + } + } + break; + + case CallDestination: + // Target of a call instruction, add the function to the analysis + if (IsOffsetExternSemanticsFast(data, externSections, info.branchTarget[i])) + { + // Deal with direct pointers into the extern section + DataVariable dataVar; + if (data->GetDataVariableAtAddress(info.branchTarget[i], dataVar) && + (dataVar.address == info.branchTarget[i]) && dataVar.type && + (dataVar.type->GetClass() == FunctionTypeClass)) + { + function.AddDirectCodeReference(location, info.branchTarget[i]); + if (!dataVar.type->CanReturn()) + { + function.AddDirectNoReturnCall(location); + endsBlock = true; + block->SetCanExit(false); + } + // No need to add the target to the calledFunctions list since a call to external code + // can never be the 'next' function + } + break; + } + + fastPath = fastValidate && (info.branchTarget[i] >= fastStartAddr) && (info.branchTarget[i] <= fastEndAddr); + if (fastPath || (data->IsValidOffset(info.branchTarget[i]) && data->IsOffsetBackedByFile(info.branchTarget[i]) && + ((!validateExecutable) || data->IsOffsetExecutable(info.branchTarget[i])))) + { + target = ArchAndAddr(info.branchArch[i] ? new CoreArchitecture(info.branchArch[i]) : location.arch, info.branchTarget[i]); + + if (!fastPath && !IsOffsetCodeSemanticsFast(data, readOnlySections, dataExternSections, target.address) && + IsOffsetCodeSemanticsFast(data, readOnlySections, dataExternSections, location.address)) + { + string message = fmt::format("Non-code call target {:#x}", target.address); + function.CreateAutoAddressTag(target.arch, location.address, "Non-code Branch", message, true); + break; + } + + Ref platform = funcPlatform; + if (target.arch != platform->GetArchitecture()) + { + platform = funcPlatform->GetRelatedPlatform(target.arch); + if (!platform) + platform = funcPlatform; + } + + // Check if valid target + if (data->ShouldSkipTargetAnalysis(location, &function, instrEnd, target)) + break; + + Ref func = data->AddFunctionForAnalysis(platform, target.address, true); + if (!func) + { + if (!data->IsOffsetBackedByFile(target.address)) + BNLogError("Function at 0x%" PRIx64 " failed to add target not backed by file.", function.GetStart()); + break; + } + + function.AddDirectCodeReference(location, target.address); + if (!func->CanReturn()) + { + function.AddDirectNoReturnCall(location); + endsBlock = true; + block->SetCanExit(false); + } + + // Add function as an early reference in case it gets updated before this + // function finishes analysis. + if (!function.HasTempOutgoingReference(func)) + { + function.AddTempOutgoingReference(func); + func->AddTempIncomingReference(&function); + } + + calledFunctions.insert(func); + } + break; + + case SystemCall: + break; + + default: + // Undefined type or target, check for targets from analysis and stop disassembling this block + endsBlock = true; + + if (info.branchType[i] == IndirectBranch) + { + // Indirect calls need not end the block early. + Ref ilFunc = new LowLevelILFunction(location.arch, nullptr); + location.arch->GetInstructionLowLevelIL(opcode, location.address, maxLen, *ilFunc); + for (size_t idx = 0; idx < ilFunc->GetInstructionCount(); idx++) + { + if ((*ilFunc)[idx].operation == LLIL_CALL) + { + endsBlock = false; + break; + } + } + } + + indirectBranchIter = indirectBranches.find(location); + endIter = indirectBranches.end(); + if (indirectBranchIter != endIter) + { + for (auto& branch : indirectBranchIter->second) + { + function.AddDirectCodeReference(location, branch.address); + Ref targetPlatform = funcPlatform; + if (branch.arch != function.GetArchitecture()) + targetPlatform = funcPlatform->GetRelatedPlatform(branch.arch); + + // Normal analysis should not inline indirect targets that are function starts + if (context.translateTailCalls && data->GetAnalysisFunction(targetPlatform, branch.address)) + continue; + + block->AddPendingOutgoingEdge(IndirectBranch, branch.address, branch.arch); + if (seenBlocks.count(branch) == 0) + { + blocksToProcess.push(branch); + seenBlocks.insert(branch); + } + } + } + else if (info.branchType[i] == ExceptionBranch) + { + block->SetCanExit(false); + } + else if (info.branchType[i] == FunctionReturn) + { + // Support for contextual function returns. This is mainly used for ARM/Thumb with 'blx lr'. It's most common for this to be treated + // as a function return, however it can also be a function call. For now this transform is described as follows: + // 1) Architecture lifts a call instruction as LLIL_CALL with a branch type of FunctionReturn + // 2) By default, contextualFunctionReturns is used to translate this to a LLIL_RET (conservative) + // 3) Downstream analysis uses dataflow to validate the return target + // 4) If the target is not the ReturnAddressValue, then we avoid the translation to a return and leave the instruction as a call + bool value; + if (function.GetContextualFunctionReturn(location, value)) + endsBlock = value; + else + { + Ref ilFunc = new LowLevelILFunction(location.arch, nullptr); + location.arch->GetInstructionLowLevelIL(opcode, location.address, maxLen, *ilFunc); + if (ilFunc->GetInstructionCount() && ((*ilFunc)[0].operation == LLIL_CALL)) + function.SetContextualFunctionReturn(location, true); + } + } + else + { + // If analysis did not find any valid branch targets, don't assume anything about global + // function state, such as __noreturn analysis, since we can't see the entire function. + block->SetUndeterminedOutgoingEdges(true); + } + break; + } + } + } + + if (function.LocationHasNoReturnCalls(location)) + { + size_t instrLength = info.length; + if (info.delaySlots) + { + InstructionInfo delayInfo; + delayInfo.delaySlots = info.delaySlots; // we'll decrement this inside the loop + size_t archMax = location.arch->GetMaxInstructionLength(); + uint8_t delayOpcode[BN_MAX_INSTRUCTION_LENGTH]; + do + { + delayInfo.delaySlots--; + if (!location.arch->GetInstructionInfo(delayOpcode, location.address + instrLength, archMax - instrLength, delayInfo)) + break; + instrLength += delayInfo.length; + } while (delayInfo.delaySlots && (instrLength < archMax)); + } + + // Conditional Call Support (Part 1) + // Do not halt basic block analysis if this is a conditional call to a function that is 'no return' + // This works for both direct and indirect calls. + // Note: Do not lift a conditional call (direct or not) with branch information. + Ref ilFunc = new LowLevelILFunction(location.arch, nullptr); + ilFunc->SetCurrentAddress(location.arch, location.address); + location.arch->GetInstructionLowLevelIL(opcode, location.address, maxLen, *ilFunc); + if (!(ilFunc->GetInstructionCount() && ((*ilFunc)[0].operation == LLIL_IF))) + { + endsBlock = true; + block->SetCanExit(false); + } + } + + location.address += info.length; + block->AddInstructionData(opcode, info.length); + + if (endsBlock && !info.delaySlots) + break; + + // Respect the 'analysis.limits.maxFunctionSize' setting while allowing for overridable behavior as well. + // We prefer to allow disassembly when function analysis is disabled, but only up to the maximum size. + // The log message and tag are generated in ProcessAnalysisSkip + totalSize += info.length; + if (context.analysisSkipOverride == NeverSkipFunctionAnalysis) + maxSize = 0; + else if (!maxSize && (context.analysisSkipOverride == AlwaysSkipFunctionAnalysis)) + maxSize = context.maxFunctionSize; + if (maxSize && (totalSize > maxSize)) + break; + + if (delaySlotCount) + { + delaySlotCount--; + if (!delaySlotCount && delayInstructionEndsBlock) + break; + } + else + { + delaySlotCount = info.delaySlots; + delayInstructionEndsBlock = endsBlock; + } + + if (block->CanExit() && context.translateTailCalls && !delaySlotCount && hasNextFunc && (location.address == nextFuncAddr)) + { + // Falling through into another function. Don't consider this a tail call if the current block + // called the function, as this indicates a get PC construct. + if (calledFunctions.count(nextFunc) == 0) + { + block->SetFallThroughToFunction(true); + if (!nextFunc->CanReturn()) + { + function.AddDirectNoReturnCall(instructionGroupStart); + block->SetCanExit(false); + } + break; + } + hasNextFunc = GetNextFunctionAfterAddress(data, funcPlatform, location.address, nextFunc); + nextFuncAddr = (hasNextFunc && nextFunc) ? nextFunc->GetStart() : 0; + } + } + + if (location.address != block->GetStart()) + { + // Block has one or more instructions, add it to the fucntion + block->SetEnd(location.address); + function.AddBasicBlock(block); + } + } + + // Finalize the function basic block list + function.FinalizeBasicBlocks(); +} + + +void Architecture::DefaultAnalyzeBasicBlocksCallback(BNFunction* function, BNBasicBlockAnalysisContext* context) +{ + Ref func(new Function(BNNewFunctionReference(function))); + Architecture::DefaultAnalyzeBasicBlocks(*func, *context); +} diff --git a/defaultabb/CMakeLists.txt b/defaultabb/CMakeLists.txt new file mode 100644 index 000000000..1594ec8ae --- /dev/null +++ b/defaultabb/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) + +project(defaultabb) + +if(NOT BN_INTERNAL_BUILD) + add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api) +endif() + +file(GLOB SOURCES + *.cpp + *.h) + +if(DEMO) + add_library(defaultabb STATIC ${SOURCES}) +else() + add_library(defaultabb SHARED ${SOURCES}) +endif() + +target_include_directories(defaultabb + PRIVATE ${PROJECT_SOURCE_DIR} +) + +if(WIN32) + target_link_directories(defaultabb + PRIVATE ${BN_INSTALL_DIR}) + target_link_libraries(defaultabb binaryninjaapi) +else() + target_link_libraries(defaultabb binaryninjaapi) +endif() + +if(UNIX AND NOT APPLE) + target_link_options(defaultabb PRIVATE "LINKER:--exclude-libs,ALL") +endif() + +set_target_properties(defaultabb 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(defaultabb) + set_target_properties(defaultabb PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +endif() diff --git a/defaultabb/plugin.cpp b/defaultabb/plugin.cpp new file mode 100644 index 000000000..917a03ee3 --- /dev/null +++ b/defaultabb/plugin.cpp @@ -0,0 +1,12 @@ +#include "binaryninjaapi.h" +#include "binaryninjacore.h" + +using namespace BinaryNinja; + +extern "C" { + BN_DECLARE_CORE_ABI_VERSION + BINARYNINJAPLUGIN bool CorePluginInit() + { + return BNArchitectureSetDefaultAnalyzeBasicBlocksCallback((void *)Architecture::DefaultAnalyzeBasicBlocksCallback); + } +} diff --git a/function.cpp b/function.cpp index 07f685a55..23813c7c0 100644 --- a/function.cpp +++ b/function.cpp @@ -319,6 +319,27 @@ vector> Function::GetBasicBlocks() const } +Ref Function::CreateBasicBlock(Architecture* arch, uint64_t addr) +{ + BNBasicBlock* block = BNCreateFunctionBasicBlock(m_object, arch->GetObject(), addr); + if (!block) + return nullptr; + return new BasicBlock(block); +} + + +void Function::AddBasicBlock(Ref block) +{ + BNAddFunctionBasicBlock(m_object, block->GetObject()); +} + + +void Function::FinalizeBasicBlocks() +{ + BNFinalizeFunctionBasicBlocks(m_object); +} + + Ref Function::GetBasicBlockAtAddress(Architecture* arch, uint64_t addr) const { BNBasicBlock* block = BNGetFunctionBasicBlockAtAddress(m_object, arch->GetObject(), addr); @@ -1780,6 +1801,78 @@ vector Function::GetIndirectBranchesAt(Architecture* arch, u } +void Function::AddDirectCodeReference(const ArchAndAddr& source, uint64_t target) +{ + BNArchitectureAndAddress bnSource; + bnSource.arch = source.arch->GetObject(); + bnSource.address = source.address; + BNFunctionAddDirectCodeReference(m_object, &bnSource, target); +} + + +void Function::AddDirectNoReturnCall(const ArchAndAddr& location) +{ + BNArchitectureAndAddress bnLocation; + bnLocation.arch = location.arch->GetObject(); + bnLocation.address = location.address; + BNFunctionAddDirectNoReturnCall(m_object, &bnLocation); +} + + +bool Function::LocationHasNoReturnCalls(const ArchAndAddr& location) const +{ + BNArchitectureAndAddress bnLocation; + bnLocation.arch = location.arch->GetObject(); + bnLocation.address = location.address; + return BNFunctionLocationHasNoReturnCalls(m_object, &bnLocation); +} + + +Ref Function::GetCalleeForAnalysis(Ref platform, uint64_t addr, bool exact) +{ + BNFunction* func = BNGetCalleeForAnalysis(m_object, platform->GetObject(), addr, exact); + if (!func) + return nullptr; + return new Function(func); +} + + +void Function::AddTempOutgoingReference(Ref target) +{ + BNFunctionAddTempOutgoingReference(m_object, target->GetObject()); +} + + +bool Function::HasTempOutgoingReference(Ref target) const +{ + return BNFunctionHasTempOutgoingReference(m_object, target->GetObject()); +} + + +void Function::AddTempIncomingReference(Ref source) +{ + BNFunctionAddTempIncomingReference(m_object, source->GetObject()); +} + + +bool Function::GetContextualFunctionReturn(const ArchAndAddr& location, bool& value) const +{ + BNArchitectureAndAddress bnLocation; + bnLocation.arch = location.arch->GetObject(); + bnLocation.address = location.address; + return BNFunctionGetContextualFunctionReturn(m_object, &bnLocation, &value); +} + + +void Function::SetContextualFunctionReturn(const ArchAndAddr& location, bool value) +{ + BNArchitectureAndAddress bnLocation; + bnLocation.arch = location.arch->GetObject(); + bnLocation.address = location.address; + BNFunctionSetContextualFunctionReturn(m_object, &bnLocation, value); +} + + vector Function::GetUnresolvedIndirectBranches() { size_t count; diff --git a/python/architecture.py b/python/architecture.py index 9dc08b040..17c9b464d 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -40,6 +40,7 @@ from . import function from . import binaryview from . import deprecation +from .variable import IndirectBranchInfo RegisterIndex = NewType('RegisterIndex', int) RegisterStackIndex = NewType('RegisterStackIndex', int) @@ -65,6 +66,16 @@ IntrinsicType = Union[IntrinsicName, 'lowlevelil.ILIntrinsic', IntrinsicIndex] +@dataclass(frozen=True) +class BasicBlockAnalysisContext: + """Used by ``analyze_basic_blocks`` and contains analysis settings and other contextual information.""" + indirect_branches: List[IndirectBranchInfo] + analysis_skip_override: core.FunctionAnalysisSkipOverrideEnum + translate_tail_calls: bool + disallow_branch_to_string: bool + max_function_size: int + + @dataclass(frozen=True) class RegisterInfo: full_width_reg: RegisterName @@ -246,6 +257,7 @@ def __init__(self): self._cb.getInstructionLowLevelIL = self._cb.getInstructionLowLevelIL.__class__( self._get_instruction_low_level_il ) + self._cb.analyzeBasicBlocks = self._cb.analyzeBasicBlocks.__class__(self._analyze_basic_blocks) self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name) self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name) self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name) @@ -710,6 +722,25 @@ def _get_instruction_low_level_il(self, ctxt, data, addr, length, il): log_error(traceback.format_exc()) return False + def _analyze_basic_blocks(self, ctx, func, ptr_bn_bb_context): + try: + bn_bb_context = ptr_bn_bb_context.contents + indirect_branches = [] + for i in range(0, bn_bb_context.indirectBranchesCount): + ibi = IndirectBranchInfo() + ibi.source_arch = CoreArchitecture._from_cache(bn_bb_context.indirectBranches[i].sourceArch) + ibi.source_addr = bn_bb_context.indirectBranches[i].sourceAddr + ibi.dest_arch = CoreArchitecture._from_cache(bn_bb_context.indirectBranches[i].destArch) + ibi.dest_addr = bn_bb_context.indirectBranches[i].destAddr + ibi.auto_defined = bn_bb_context.indirectBranches[i].autoDefined + indirect_branches.append(ibi) + + context = BasicBlockAnalysisContext(indirect_branches, bn_bb_context.analysisSkipOverride, + bn_bb_context.translateTailCalls, bn_bb_context.disallowBranchToString, bn_bb_context.maxFunctionSize) + self.analyze_basic_blocks(function.Function(handle=core.BNNewFunctionReference(func)), context) + except: + log_error(traceback.format_exc()) + def _get_register_name(self, ctxt, reg): try: if reg in self._regs_by_index: @@ -1425,6 +1456,34 @@ def get_instruction_low_level_il(self, data: bytes, addr: int, il: lowlevelil.Lo """ raise NotImplementedError + def analyze_basic_blocks(self, func: 'function.Function', context: BasicBlockAnalysisContext) -> None: + """ + ``analyze_basic_blocks`` performs basic block recovery and commits the results to the function analysis + + .. note:: Architecture subclasses should only implement this method if function-level analysis is required + + :param Function func: the function to analyze + :param BNBasicBlockAnalysisContext context: the analysis context + """ + + try: + bn_bb_context = core.BNBasicBlockAnalysisContext() + bn_bb_context.indirectBranchesCount = len(context.indirect_branches) + bn_bb_context.analysisSkipOverride = context.analysis_skip_override + bn_bb_context.translateTailCalls = context.translate_tail_calls + bn_bb_context.disallowBranchToString = context.disallow_branch_to_string + bn_bb_context.maxFunctionSize = context.max_function_size + bn_bb_context.indirectBranches = (core.BNIndirectBranchInfo * len(context.indirect_branches))() + for i in range(0, len(context.indirect_branches)): + bn_bb_context.indirectBranches[i].sourceArch = context.indirect_branches[i].source_arch.handle + bn_bb_context.indirectBranches[i].sourceAddr = context.indirect_branches[i].source_addr + bn_bb_context.indirectBranches[i].destArch = context.indirect_branches[i].dest_arch.handle + bn_bb_context.indirectBranches[i].destAddr = context.indirect_branches[i].dest_addr + bn_bb_context.indirectBranches[i].autoDefined = context.indirect_branches[i].auto_defined + core.BNArchitectureDefaultAnalyzeBasicBlocks(func.handle, ctypes.byref(bn_bb_context)) + except: + log_error(traceback.format_exc()) + def get_low_level_il_from_bytes(self, data: bytes, addr: int) -> 'lowlevelil.LowLevelILInstruction': """ ``get_low_level_il_from_bytes`` converts the instruction in bytes to ``il`` at the given virtual address diff --git a/python/basicblock.py b/python/basicblock.py index b7182c7a1..c62203470 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -61,6 +61,22 @@ def __repr__(self): return f"<{self.type.name}: {self.target.start:#x}>" +@dataclass(frozen=True) +class PendingBasicBlockEdge: + """ + ``class PendingBasicBlockEdge`` represents a pending edge that has not yet been resolved. + + :cvar type: The edge branch type. + :cvar arch: The architecture of the target basic block. + :cvar target: The address of the target basic block. + :cvar fall_through: Whether this edge is a fallthrough edge. + """ + type: BranchType + arch: 'architecture.Architecture' + target: int + fallthrough: bool + + class BasicBlock: """ The ``class BasicBlock`` object is returned during analysis and should not be directly instantiated. @@ -350,6 +366,14 @@ def end(self) -> int: """Basic block end (read-only)""" return core.BNGetBasicBlockEnd(self.handle) + @end.setter + def end(self, value: int) -> None: + """Sets the end of the basic block + + .. note:: This setter is intended for use by architecture plugins only. + """ + core.BNSetBasicBlockEnd(self.handle, value) + @property def length(self) -> int: """Basic block length (read-only)""" @@ -397,6 +421,11 @@ def has_undetermined_outgoing_edges(self) -> bool: """Whether basic block has undetermined outgoing edges (read-only)""" return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) + @has_undetermined_outgoing_edges.setter + def has_undetermined_outgoing_edges(self, value: bool) -> None: + """Sets whether basic block has undetermined outgoing edges""" + core.BNBasicBlockSetUndeterminedOutgoingEdges(self.handle, value) + @property def can_exit(self) -> bool: """Whether basic block can return or is tagged as 'No Return' (read-only)""" @@ -412,6 +441,106 @@ def has_invalid_instructions(self) -> bool: """Whether basic block has any invalid instructions (read-only)""" return core.BNBasicBlockHasInvalidInstructions(self.handle) + @has_invalid_instructions.setter + def has_invalid_instructions(self, value: bool) -> None: + """Sets whether basic block has any invalid instructions""" + core.BNBasicBlockSetHasInvalidInstructions(self.handle, value) + + def add_pending_outgoing_edge(self, typ: BranchType, addr: int, arch: 'architecture.Architecture', fallthrough: bool = False) -> None: + """ + Adds a pending outgoing edge to the basic block. This is used to add edges that are not yet resolved. + + .. note:: This method is intended for use by architecture plugins only. + + :param BranchType typ: The type of the branch. + :param int addr: The address of the target basic block. + :param Architecture arch: The architecture of the target basic block. + :param bool fallthrough: Whether this edge is a fallthrough edge. + """ + + core.BNBasicBlockAddPendingOutgoingEdge(self.handle, typ.value, addr, arch.handle, fallthrough) + + def get_pending_outgoing_edges(self) -> list[PendingBasicBlockEdge]: + """ + Returns a list of pending outgoing edges for the basic block. These are edges that have not yet been resolved. + + .. note:: This method is intended for use by architecture plugins only. + + :return: List of PendingBasicBlockEdge objects. + :rtype: list[PendingBasicBlockEdge] + """ + count = ctypes.c_ulonglong(0) + pending_edges = core.BNGetBasicBlockPendingOutgoingEdges(self.handle, ctypes.byref(count)) + if pending_edges is None: + return [] + + result: List[PendingBasicBlockEdge] = [] + try: + for i in range(count.value): + result.append(PendingBasicBlockEdge( + type=BranchType(pending_edges[i].type), + arch=architecture.CoreArchitecture._from_cache(pending_edges[i].arch), + target=pending_edges[i].target, + fallthrough=pending_edges[i].fallThrough + )) + return result + finally: + core.BNFreePendingBasicBlockEdgeList(pending_edges) + + def clear_pending_outgoing_edges(self) -> None: + """ + Clears all pending outgoing edges for the basic block. This is used to remove edges that have not yet been resolved. + + .. note:: This method is intended for use by architecture plugins only. + """ + core.BNClearBasicBlockPendingOutgoingEdges(self.handle) + + def get_instruction_data(self, addr: int) -> bytes: + """ + Returns the raw instruction data for the basic block at the specified address. + + .. note:: This method is intended for use by architecture plugins only. + + :return: Raw instruction data as bytes. + :rtype: bytes + """ + + size = ctypes.c_ulonglong(0) + data = core.BNBasicBlockGetInstructionData(self.handle, addr, ctypes.byref(size)) + if data is None: + return b'' + + return ctypes.string_at(data, size.value) + + def add_instruction_data(self, data: bytes) -> None: + """ + Adds raw instruction data to the basic block. + + .. note:: This method is intended for use by architecture plugins only. + + :param bytes data: Raw instruction data to add to the basic block. + """ + if not isinstance(data, bytes): + raise TypeError("data must be of type bytes") + + core.BNBasicBlockAddInstructionData(self.handle, data, len(data)) + + @property + def fallthrough_to_function(self) -> bool: + """Whether the basic block has a fallthrough edge to a function.""" + + return core.BNBasicBlockIsFallThroughToFunction(self.handle) + + @fallthrough_to_function.setter + def fallthrough_to_function(self, value: bool) -> None: + """Sets whether the basic block has a fallthrough edge to a function. + + .. note:: This setter is intended for use by architecture plugins only. + """ + if not isinstance(value, bool): + raise TypeError("value must be of type bool") + core.BNBasicBlockSetFallThroughToFunction(self.handle, value) + def _make_blocks(self, blocks, count: int) -> List['BasicBlock']: assert blocks is not None, "core returned empty block list" try: diff --git a/python/binaryview.py b/python/binaryview.py index 092f9bef1..5762c6e8d 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -4952,6 +4952,43 @@ def abort_analysis(self) -> None: """ core.BNAbortAnalysis(self.handle) + @property + def analysis_is_aborted(self) -> bool: + """ + ``analysis_is_aborted`` checks if the analysis has been aborted. + + .. note:: This property is intended for use by architecture plugins only. + + :return: True if the analysis has been aborted, False otherwise + :rtype: bool + """ + + return core.BNAnalysisIsAborted(self.handle) + + def should_skip_target_analysis(self, source_location: '_function.ArchAndAddr', source_function: '_function.Function', + end: int, target_location: '_function.ArchAndAddr') -> bool: + """ + ``should_skip_target_analysis`` checks if target analysis should be skipped. + + .. note:: This method is intended for use by architecture plugins only. + + :param _function.ArchAndAddr source_location: The source location. + :param _function.Function source_function: The source function. + :param int end: The end address of the source branch instruction. + :param _function.ArchAndAddr target_location: The target location. + :return: True if the target analysis should be skipped, False otherwise + :rtype: bool + """ + + bn_src_arch_and_addr = core.BNArchitectureAndAddress() + bn_src_arch_and_addr.arch = source_location.arch.handle + bn_src_arch_and_addr.address = source_location.addr + bn_target_arch_and_addr = core.BNArchitectureAndAddress() + bn_target_arch_and_addr.arch = target_location.arch.handle + bn_target_arch_and_addr.address = target_location.addr + return core.BNShouldSkipTargetAnalysis(self.handle, bn_src_arch_and_addr, source_function.handle, end, + bn_target_arch_and_addr) + def define_data_var( self, addr: int, var_type: StringOrType, name: Optional[Union[str, '_types.CoreSymbol']] = None ) -> None: diff --git a/python/function.py b/python/function.py index be6690026..539050a40 100644 --- a/python/function.py +++ b/python/function.py @@ -83,7 +83,6 @@ def _function_name_(): return inspect.stack()[1][0].f_code.co_name - @dataclass(frozen=True) class ArchAndAddr: arch: 'architecture.Architecture' @@ -2521,6 +2520,185 @@ def get_basic_block_at(self, addr: int, return None return basicblock.BasicBlock(block, self._view) + def create_basic_block(self, arch: 'architecture.Architecture', addr: int) -> 'basicblock.BasicBlock': + """ + ``create_basic_block`` creates a new BasicBlock at the specified address for the given Architecture. + + .. note:: This method is intended for use by architecture plugins only. + + :param Architecture arch: Architecture of the BasicBlock to create + :param int addr: Address of the BasicBlock to create + :rtype: basicblock.BasicBlock + """ + if not isinstance(arch, architecture.Architecture): + raise TypeError("arch must be an instance of architecture.Architecture") + + bnblock = core.BNCreateFunctionBasicBlock(self.handle, arch.handle, addr) + if not bnblock: + return None + + return basicblock.BasicBlock(bnblock, self._view) + + def add_basic_block(self, block: 'basicblock.BasicBlock') -> None: + """ + ``add_basic_block`` adds the specified BasicBlock to the Function. + + .. note:: This method is intended for use by architecture plugins only. + + :param basicblock.BasicBlock block: BasicBlock to add to the Function + :rtype: None + """ + if not isinstance(block, basicblock.BasicBlock): + raise TypeError("block must be an instance of basicblock.BasicBlock") + + core.BNAddFunctionBasicBlock(self.handle, block.handle) + + def finalize_basic_blocks(self) -> None: + """ + ``finalize_basic_blocks`` finalizes the BasicBlocks in the Function, ensuring they are committed to analysis. + + .. note:: This method is intended for use by architecture plugins only. + + :rtype: None + """ + core.BNFinalizeFunctionBasicBlocks(self.handle) + + def add_direct_code_reference(self, source: ArchAndAddr, target: int) -> None: + """ + ``add_direct_code_reference`` adds a direct code reference from the source to the target address. + + .. note:: This method is intended for use by architecture plugins only. + + :param ArchAndAddr source: Source address and architecture of the reference + :param int target: Target address of the reference + :rtype: None + """ + bn_arch_and_addr = core.BNArchitectureAndAddress() + bn_arch_and_addr.arch = source.arch.handle + bn_arch_and_addr.address = source.addr + core.BNFunctionAddDirectCodeReference( + self.handle, bn_arch_and_addr, target + ) + + def add_direct_no_return_call(self, location: ArchAndAddr) -> None: + """ + ``add_direct_no_return_call`` adds a direct no return call site to the function at the specified location. + + .. note:: This method is intended for use by architecture plugins only. + + :param ArchAndAddr location: Location of the call site + :rtype: None + """ + bn_arch_and_addr = core.BNArchitectureAndAddress() + bn_arch_and_addr.arch = location.arch.handle + bn_arch_and_addr.address = location.addr + core.BNFunctionAddDirectNoReturnCall(self.handle, bn_arch_and_addr) + + def location_has_no_return_calls(self, location: ArchAndAddr) -> bool: + """ + ``location_has_no_return_calls`` checks if the specified location has any no return calls sites. + + .. note:: This method is intended for use by architecture plugins only. + + :param ArchAndAddr location: Location to check for no return calls + :return: True if there are no return calls at the specified location, False otherwise + :rtype: bool + """ + bn_arch_and_addr = core.BNArchitectureAndAddress() + bn_arch_and_addr.arch = location.arch.handle + bn_arch_and_addr.address = location.addr + return core.BNFunctionLocationHasNoReturnCalls(self.handle, bn_arch_and_addr) + + def get_callee_for_analysis(self, platform: 'platform.Platform', addr: int, exact: bool = False) -> Optional['Function']: + """ + ``get_callee_for_analysis`` retrieves the callee function for the specified address and platform. + + .. note:: This method is intended for use by architecture plugins only. + + :param platform.Platform platform: Platform of the callee function + :param int addr: Address of the callee function + :param bool exact: If True, only return a function if it exactly matches the address and platform + :return: The callee function or None if not found + :rtype: Optional[Function] + """ + func = core.BNGetCalleeForAnalysis(self.handle, platform.handle, addr, exact) + if func is None: + return None + return Function(func, self._view) + + def add_temp_outgoing_reference(self, target: 'Function') -> None: + """ + ``add_temp_outgoing_reference`` adds a temporary outgoing reference to the specified target function. + + .. note:: This method is intended for use by architecture plugins only. + + :param Function target: Target function to add a temporary outgoing reference to + :rtype: None + """ + if not isinstance(target, Function): + raise TypeError("target must be an instance of Function") + core.BNFunctionAddTempOutgoingReference(self.handle, target.handle) + + def has_temp_outgoing_reference(self, target: 'Function') -> bool: + """ + ``has_temp_outgoing_reference`` checks if the function has a temporary outgoing reference to the specified target function. + + .. note:: This method is intended for use by architecture plugins only. + + :param Function target: Target function to check for a temporary outgoing reference + :return: True if there is a temporary outgoing reference to the target function, False otherwise + :rtype: bool + """ + if not isinstance(target, Function): + raise TypeError("target must be an instance of Function") + return core.BNFunctionHasTempOutgoingReference(self.handle, target.handle) + + def add_temp_incoming_reference(self, source: 'Function') -> None: + """ + ``add_temp_incoming_reference`` adds a temporary incoming reference from the specified source function. + + .. note:: This method is intended for use by architecture plugins only. + + :param Function source: Source function to add a temporary incoming reference from + :rtype: None + """ + if not isinstance(source, Function): + raise TypeError("source must be an instance of Function") + core.BNFunctionAddTempIncomingReference(self.handle, source.handle) + + def get_contextual_function_return(self, location: ArchAndAddr) -> tuple[bool, bool]: + """ + ``get_contextual_function_return`` checks if the specified location has a contextual function return. + + .. note:: This method is intended for use by architecture plugins only. + + :param ArchAndAddr location: Location to check for a contextual function return + :return: A tuple containing a bool indicating whether the location has a contextual function return, and a bool containing the value that was set + :rtype: tuple[bool, bool] + """ + bn_arch_and_addr = core.BNArchitectureAndAddress() + bn_arch_and_addr.arch = location.arch.handle + bn_arch_and_addr.address = location.addr + result = ctypes.c_bool() + if not core.BNFunctionGetContextualFunctionReturn(self.handle, bn_arch_and_addr, ctypes.byref(result)): + return (False, False) + return (True, result.value) + + def set_contextual_function_return(self, location: ArchAndAddr, value: bool) -> None: + """ + ``set_contextual_function_return`` sets a contextual function return for the specified location. + + .. note:: This method is intended for use by architecture plugins only. + + :param ArchAndAddr location: Location to set the contextual function return for + :param bool value: Value to set for the contextual function return + :rtype: None + """ + bn_arch_and_addr = core.BNArchitectureAndAddress() + bn_arch_and_addr.arch = location.arch.handle + bn_arch_and_addr.address = location.addr + core.BNFunctionSetContextualFunctionReturn(self.handle, bn_arch_and_addr, value) + def get_instr_highlight( self, addr: int, arch: Optional['architecture.Architecture'] = None ) -> '_highlight.HighlightColor': diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 3573f1fb2..907d8a6c6 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -31,6 +31,7 @@ use crate::{ string::*, types::{NameAndType, Type}, Endianness, + function::Function, }; use std::ops::Deref; use std::{ @@ -463,6 +464,19 @@ pub trait Architecture: 'static + Sized + AsRef { il: &mut MutableLiftedILFunction, ) -> Option<(usize, bool)>; + fn analyze_basic_blocks( + &self, + function: &mut Function, + context: *mut BNBasicBlockAnalysisContext, + ) { + unsafe { + BNArchitectureDefaultAnalyzeBasicBlocks( + function.handle, + context, + ); + }; + } + /// Fallback flag value calculation path. This method is invoked when the core is unable to /// recover flag use semantics, and resorts to emitting instructions that explicitly set each /// observed flag to the value of an expression returned by this function. @@ -1525,6 +1539,20 @@ impl Architecture for CoreArchitecture { } } + fn analyze_basic_blocks( + &self, + function: &mut Function, + context: *mut BNBasicBlockAnalysisContext, + ) { + unsafe { + BNArchitectureAnalyzeBasicBlocks( + self.handle, + function.handle, + context, + ); + }; + } + fn flag_write_llil<'a>( &self, _flag: Self::Flag, @@ -2234,6 +2262,19 @@ where } } + extern "C" fn cb_analyze_basic_blocks( + ctxt: *mut c_void, + function: *mut BNFunction, + context: *mut BNBasicBlockAnalysisContext, + ) + where + A: 'static + Architecture> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let mut function = unsafe { Function::from_raw(function) }; + custom_arch.analyze_basic_blocks(&mut function, context); + } + extern "C" fn cb_reg_name(ctxt: *mut c_void, reg: u32) -> *mut c_char where A: 'static + Architecture> + Send + Sync, @@ -3158,6 +3199,7 @@ where getInstructionText: Some(cb_get_instruction_text::), freeInstructionText: Some(cb_free_instruction_text), getInstructionLowLevelIL: Some(cb_instruction_llil::), + analyzeBasicBlocks: Some(cb_analyze_basic_blocks::), getRegisterName: Some(cb_reg_name::), getFlagName: Some(cb_flag_name::),