summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--architecture.cpp23
-rw-r--r--basicblock.cpp81
-rw-r--r--binaryninjaapi.h162
-rw-r--r--binaryninjacore.h77
-rw-r--r--binaryview.cpp20
-rw-r--r--defaultabb.cpp664
-rw-r--r--defaultabb/CMakeLists.txt50
-rw-r--r--defaultabb/plugin.cpp12
-rw-r--r--function.cpp93
-rw-r--r--python/architecture.py59
-rw-r--r--python/basicblock.py129
-rw-r--r--python/binaryview.py37
-rw-r--r--python/function.py180
-rw-r--r--rust/src/architecture.rs42
14 files changed, 1617 insertions, 12 deletions
diff --git a/architecture.cpp b/architecture.cpp
index 22bf6fc3..22045ee2 100644
--- a/architecture.cpp
+++ b/architecture.cpp
@@ -24,6 +24,7 @@
#include <inttypes.h>
#include <vector>
#include "binaryninjaapi.h"
+#include "lowlevelilinstruction.h"
#include "ffi.h"
using namespace BinaryNinja;
@@ -327,6 +328,15 @@ bool Architecture::GetInstructionLowLevelILCallback(
}
+void Architecture::AnalyzeBasicBlocksCallback(void *ctxt, BNFunction* function,
+ BNBasicBlockAnalysisContext* context)
+{
+ CallbackRef<Architecture> arch(ctxt);
+ Ref<Function> func(new Function(BNNewFunctionReference(function)));
+ arch->AnalyzeBasicBlocks(*func, *context);
+}
+
+
char* Architecture::GetRegisterNameCallback(void* ctxt, uint32_t reg)
{
CallbackRef<Architecture> arch(ctxt);
@@ -817,6 +827,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;
@@ -945,6 +956,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);
@@ -1505,6 +1522,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 28ee393b..e178bd6e 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<Architecture> arch, bool fallThrough)
+{
+ BNBasicBlockAddPendingOutgoingEdge(m_object, type, addr, arch ? arch->GetObject() : nullptr, fallThrough);
+}
+
+
+vector<PendingBasicBlockEdge> BasicBlock::GetPendingOutgoingEdges() const
+{
+ size_t count;
+ BNPendingBasicBlockEdge* edges = BNGetBasicBlockPendingOutgoingEdges(m_object, &count);
+ vector<PendingBasicBlockEdge> 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 920d2eb0..74d30d92 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -359,6 +359,10 @@ namespace BinaryNinja {
bool operator<(const Ref<T>& 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<T>& obj) const { return T::GetObject(m_obj) > T::GetObject(obj.m_obj); }
+
T* GetPtr() const { return m_obj; }
};
@@ -579,6 +583,7 @@ namespace BinaryNinja {
class FlowGraph;
class ReportCollection;
struct FormInputField;
+ struct ArchAndAddr;
/*! Logs to the error console with the given BNLogLevel.
@@ -5303,6 +5308,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
@@ -6899,6 +6911,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<Function> 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
@@ -8050,6 +8073,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.
@@ -8078,6 +8102,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);
@@ -8150,6 +8175,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
@@ -8248,6 +8282,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
@@ -8526,7 +8567,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<RelocationHandler> GetRelocationHandler(const std::string& viewName);
@@ -8642,6 +8682,7 @@ namespace BinaryNinja {
const uint8_t* data, uint64_t addr, size_t& len, std::vector<InstructionTextToken>& 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;
@@ -10486,6 +10527,17 @@ namespace BinaryNinja {
/*!
\ingroup basicblocks
*/
+ struct PendingBasicBlockEdge
+ {
+ BNBranchType type;
+ Ref<Architecture> arch;
+ uint64_t target;
+ bool fallThrough;
+ };
+
+ /*!
+ \ingroup basicblocks
+ */
class BasicBlock : public CoreRefCountObject<BNBasicBlock, BNNewBasicBlockReference, BNFreeBasicBlock>
{
public:
@@ -10509,6 +10561,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
@@ -10545,6 +10604,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<Architecture> arch = nullptr,
+ bool fallThrough = false);
+
+ /*! Get a list of pending outgoing edges for this basic block
+
+ \return List of pending outgoing edges
+ */
+ std::vector<PendingBasicBlockEdge> 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'
@@ -10791,12 +10916,14 @@ 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)
return true;
- if (arch > a.arch)
+ if (a.arch < arch)
return false;
return address < a.address;
}
@@ -10936,6 +11063,24 @@ namespace BinaryNinja {
*/
std::vector<Ref<BasicBlock>> 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<BasicBlock> 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<BasicBlock> 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
@@ -11266,6 +11411,17 @@ namespace BinaryNinja {
std::vector<IndirectBranchInfo> GetIndirectBranches();
std::vector<IndirectBranchInfo> 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<Function> GetCalleeForAnalysis(Ref<Platform> platform, uint64_t addr, bool exact);
+ void AddTempOutgoingReference(Ref<Function> target);
+ bool HasTempOutgoingReference(Ref<Function> target) const;
+ void AddTempIncomingReference(Ref<Function> source);
+
+ bool GetContextualFunctionReturn(const ArchAndAddr& location, bool& value) const;
+ void SetContextualFunctionReturn(const ArchAndAddr& location, bool value);
+
std::vector<uint64_t> GetUnresolvedIndirectBranches();
bool HasUnresolvedIndirectBranches();
diff --git a/binaryninjacore.h b/binaryninjacore.h
index f94d4de6..9d84cf2a 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -307,6 +307,8 @@ extern "C"
typedef struct BNLineFormatter BNLineFormatter;
typedef struct BNRenderLayer BNRenderLayer;
typedef struct BNStringRef BNStringRef;
+ typedef struct BNIndirectBranchInfo BNIndirectBranchInfo;
+ typedef struct BNArchitectureAndAddress BNArchitectureAndAddress;
typedef bool(*BNProgressFunction)(void*, size_t, size_t);
@@ -1856,6 +1858,30 @@ 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;
+ bool haltOnInvalidInstructions;
+ uint64_t maxFunctionSize;
+
+ // OUT
+ bool maxSizeReached;
+
+ BNArchitectureAndAddress* haltedDisassemblyAddresses;
+ size_t haltedDisassemblyAddressesCount;
+ } BNBasicBlockAnalysisContext;
+
typedef struct BNCustomArchitecture
{
void* context;
@@ -1874,6 +1900,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);
@@ -1980,6 +2007,14 @@ extern "C"
bool fallThrough;
} BNBasicBlockEdge;
+ typedef struct BNPendingBasicBlockEdge
+ {
+ BNBranchType type;
+ BNArchitecture* arch;
+ uint64_t target;
+ bool fallThrough;
+ } BNPendingBasicBlockEdge;
+
typedef struct BNPoint
{
float x;
@@ -3145,13 +3180,6 @@ extern "C"
uint8_t confidence;
} BNRegisterStackAdjustment;
- typedef enum BNFunctionAnalysisSkipOverride
- {
- DefaultFunctionAnalysisSkip,
- NeverSkipFunctionAnalysis,
- AlwaysSkipFunctionAnalysis
- } BNFunctionAnalysisSkipOverride;
-
typedef enum BNReportType
{
PlainTextReportType,
@@ -4501,6 +4529,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);
@@ -4616,6 +4648,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);
@@ -4679,6 +4712,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);
@@ -4816,14 +4852,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);
@@ -5048,6 +5096,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);
@@ -5117,13 +5177,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 d8477826..66ef0e10 100644
--- a/binaryview.cpp
+++ b/binaryview.cpp
@@ -2186,6 +2186,12 @@ void BinaryView::AbortAnalysis()
}
+bool BinaryView::AnalysisIsAborted() const
+{
+ return BNAnalysisIsAborted(m_object);
+}
+
+
void BinaryView::DefineDataVariable(uint64_t addr, const Confidence<Ref<Type>>& type)
{
BNTypeWithConfidence tc;
@@ -5268,6 +5274,20 @@ void BinaryView::SetNewAutoFunctionAnalysisSuppressed(bool suppress)
}
+bool BinaryView::ShouldSkipTargetAnalysis(const ArchAndAddr& source, Ref<Function> 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<NameSpace> BinaryView::GetNameSpaces() const
{
set<NameSpace> nameSpaces;
diff --git a/defaultabb.cpp b/defaultabb.cpp
new file mode 100644
index 00000000..c6592fc0
--- /dev/null
+++ b/defaultabb.cpp
@@ -0,0 +1,664 @@
+#include <map>
+#include <set>
+#include <queue>
+#include <inttypes.h>
+#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<Section*>& readOnlySections, const vector<Section*>& 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<Section*>& 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<BinaryView> data, Ref<Platform> platform, uint64_t address, Ref<Function>& 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<ArchAndAddr> blocksToProcess;
+ map<ArchAndAddr, Ref<BasicBlock>> instrBlocks;
+ set<ArchAndAddr> seenBlocks;
+ map<ArchAndAddr, set<ArchAndAddr>> 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<Section*> 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> 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;
+ bool maxSizeReached = false;
+ 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<BasicBlock> block = function.CreateBasicBlock(location.arch, location.address);
+
+ // Get the next function to prevent disassembling into the next function if the block falls through
+ Ref<Function> nextFunc;
+ bool hasNextFunc = GetNextFunctionAfterAddress(data, funcPlatform, location.address, nextFunc);
+ uint64_t nextFuncAddr = (hasNextFunc && nextFunc) ? nextFunc->GetStart() : 0;
+ set<Ref<Function>> 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<BasicBlock> 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<BasicBlock> 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<ArchAndAddr, set<ArchAndAddr>>::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> 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<Function> 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<LowLevelILFunction> 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<Platform> 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<LowLevelILFunction> 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<LowLevelILFunction> 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))
+ {
+ maxSizeReached = true;
+ 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);
+ }
+
+ if (maxSizeReached)
+ break;
+
+ if (context.haltOnInvalidInstructions && block->HasInvalidInstructions())
+ {
+ // TODO add haltedDisassemblyAddresses
+ }
+ }
+
+ // Finalize the function basic block list
+ function.FinalizeBasicBlocks();
+
+ if (maxSizeReached)
+ context.maxSizeReached = true;
+}
+
+
+void Architecture::DefaultAnalyzeBasicBlocksCallback(BNFunction* function, BNBasicBlockAnalysisContext* context)
+{
+ Ref<Function> func(new Function(BNNewFunctionReference(function)));
+ Architecture::DefaultAnalyzeBasicBlocks(*func, *context);
+}
diff --git a/defaultabb/CMakeLists.txt b/defaultabb/CMakeLists.txt
new file mode 100644
index 00000000..1594ec8a
--- /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 00000000..917a03ee
--- /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 2e6b702e..0a4337df 100644
--- a/function.cpp
+++ b/function.cpp
@@ -319,6 +319,27 @@ vector<Ref<BasicBlock>> Function::GetBasicBlocks() const
}
+Ref<BasicBlock> 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<BasicBlock> block)
+{
+ BNAddFunctionBasicBlock(m_object, block->GetObject());
+}
+
+
+void Function::FinalizeBasicBlocks()
+{
+ BNFinalizeFunctionBasicBlocks(m_object);
+}
+
+
Ref<BasicBlock> Function::GetBasicBlockAtAddress(Architecture* arch, uint64_t addr) const
{
BNBasicBlock* block = BNGetFunctionBasicBlockAtAddress(m_object, arch->GetObject(), addr);
@@ -1787,6 +1808,78 @@ vector<IndirectBranchInfo> 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> Function::GetCalleeForAnalysis(Ref<Platform> 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<Function> target)
+{
+ BNFunctionAddTempOutgoingReference(m_object, target->GetObject());
+}
+
+
+bool Function::HasTempOutgoingReference(Ref<Function> target) const
+{
+ return BNFunctionHasTempOutgoingReference(m_object, target->GetObject());
+}
+
+
+void Function::AddTempIncomingReference(Ref<Function> 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<uint64_t> Function::GetUnresolvedIndirectBranches()
{
size_t count;
diff --git a/python/architecture.py b/python/architecture.py
index 29a393a8..8ce4b4f6 100644
--- a/python/architecture.py
+++ b/python/architecture.py
@@ -40,6 +40,7 @@ from . import typelibrary
from . import function
from . import binaryview
from . import deprecation
+from .variable import IndirectBranchInfo
RegisterIndex = NewType('RegisterIndex', int)
RegisterStackIndex = NewType('RegisterStackIndex', int)
@@ -66,6 +67,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
size: int
@@ -246,6 +257,7 @@ class Architecture(metaclass=_ArchitectureMetaClass):
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)
@@ -711,6 +723,25 @@ class Architecture(metaclass=_ArchitectureMetaClass):
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:
@@ -1426,6 +1457,34 @@ class Architecture(metaclass=_ArchitectureMetaClass):
"""
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 cd9227d5..1148d887 100644
--- a/python/basicblock.py
+++ b/python/basicblock.py
@@ -61,6 +61,22 @@ class BasicBlockEdge:
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.
@@ -361,6 +377,14 @@ class BasicBlock:
"""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)"""
@@ -408,6 +432,11 @@ class BasicBlock:
"""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)"""
@@ -423,6 +452,106 @@ class BasicBlock:
"""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 a03cb7bc..4761758a 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -4972,6 +4972,43 @@ class BinaryView:
"""
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 126892ec..2750e40a 100644
--- a/python/function.py
+++ b/python/function.py
@@ -85,7 +85,6 @@ FunctionViewTypeOrName = Union['FunctionViewType', FunctionGraphType, str]
def _function_name_():
return inspect.stack()[1][0].f_code.co_name
-
@dataclass(frozen=True)
class ArchAndAddr:
arch: 'architecture.Architecture'
@@ -2614,6 +2613,185 @@ class Function:
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 41150840..c716bb95 100644
--- a/rust/src/architecture.rs
+++ b/rust/src/architecture.rs
@@ -30,6 +30,7 @@ use crate::{
string::*,
types::{NameAndType, Type},
Endianness,
+ function::Function,
};
use std::ops::Deref;
use std::{
@@ -470,6 +471,19 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
il: &LowLevelILMutableFunction,
) -> 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.
@@ -1533,6 +1547,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,
@@ -2237,6 +2265,19 @@ where
}
}
+ extern "C" fn cb_analyze_basic_blocks<A>(
+ ctxt: *mut c_void,
+ function: *mut BNFunction,
+ context: *mut BNBasicBlockAnalysisContext,
+ )
+ where
+ A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + 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<A>(ctxt: *mut c_void, reg: u32) -> *mut c_char
where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
@@ -3155,6 +3196,7 @@ where
getInstructionText: Some(cb_get_instruction_text::<A>),
freeInstructionText: Some(cb_free_instruction_text),
getInstructionLowLevelIL: Some(cb_instruction_llil::<A>),
+ analyzeBasicBlocks: Some(cb_analyze_basic_blocks::<A>),
getRegisterName: Some(cb_reg_name::<A>),
getFlagName: Some(cb_flag_name::<A>),