diff --git a/.gitmodules b/.gitmodules index 80c4c6037c..11e1c701a4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,9 @@ [submodule "plugins/snippets"] path = plugins/snippets url = https://github.com/Vector35/snippets.git +[submodule "vendor/bindiff"] + path = vendor/bindiff + url = https://github.com/Vector35/google_bindiff.git +[submodule "vendor/binexport"] + path = vendor/binexport + url = https://github.com/Vector35/binexport.git diff --git a/base/strong_typedef.h b/base/strong_typedef.h index 1a2db87d00..136b266dcb 100644 --- a/base/strong_typedef.h +++ b/base/strong_typedef.h @@ -22,6 +22,7 @@ #include // IWYU pragma: keep #include +#include #include #include #include @@ -89,6 +90,8 @@ // Hashable usable as a key in std and absl hash containers // Formattable formattable with fmt, forwarding format specs such as {:#x} to the // underlying type's formatter +// FfiWrapper implicit conversion to and from a C-style wrapper structure through +// the specified member, without making the underlying type implicit // NonExtractable removes the explicit operator T() and the Value() accessor, so the // underlying value can be constructed but never read back out. Every // other modifier continues to function as normal. @@ -115,6 +118,25 @@ namespace detail { template inline constexpr bool HasModifier = (std::is_same_v || ...); +template +concept FfiWrapperFor = requires(const Ffi& value) { + typename Mod::FfiType; + requires std::same_as; + { Mod::template FromFfi(value) } -> std::same_as; +}; + +template +inline constexpr bool HasFfiWrapper = (FfiWrapperFor || ...); + +template +constexpr T FromFfi(const Ffi& value) +{ + if constexpr (FfiWrapperFor) + return First::template FromFfi(value); + else + return FromFfi(value); +} + // Internal access to a StrongTypedef's underlying value so that modifiers have // access to it even when NonExtractable is in use. struct Access @@ -409,6 +431,33 @@ struct Formattable }; }; +// Enables implicit conversion to and from a C-style wrapper structure whose selected +// member stores the StrongTypedef's underlying value. Conversion to the underlying type +// itself remains explicit. +template +struct FfiWrapper +{ + using FfiType = Ffi; + + template + static constexpr T FromFfi(const Ffi& value) + { + return T(value.*ValueMember); + } + + template + requires requires(Ffi ffi, const T& value) { ffi.*ValueMember = value; } + struct Apply + { + constexpr operator Ffi() const + { + Ffi result {}; + result.*ValueMember = detail::Access::Get(static_cast(*this)); + return result; + } + }; +}; + // Disable both the explicit operator T() and the Value() accessor. // All other modifiers continue to function as normal. struct NonExtractable @@ -437,6 +486,13 @@ class BN_EMPTY_BASES StrongTypedef : public Mods::template Apply + requires detail::HasFfiWrapper, T, Mods...> + constexpr StrongTypedef(Ffi&& value) + : m_value(detail::FromFfi, Mods...>(value)) + { + } + explicit constexpr operator T() const noexcept(std::is_nothrow_copy_constructible_v) requires (!detail::HasModifier) { diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 06a2019226..455a6e4c60 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -28,6 +28,7 @@ #endif #include "base/compiler.h" +#include "base/strong_typedef.h" #include "binaryninjacore.h" #include "exceptions.h" @@ -36,6 +37,7 @@ #include "vendor/nlohmann/json.hpp" #include +#include #include #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +74,8 @@ #endif namespace BinaryNinja { + namespace st = ::bn::base::strong_typedef; + #ifdef __GNUC__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ static inline uint16_t ToLE16(uint16_t val) { return val; } @@ -623,6 +628,7 @@ namespace BinaryNinja { class InteractionHandler; class QualifiedName; class FlowGraph; + class LinearViewObject; class ReportCollection; struct FormInputField; struct ArchAndAddr; @@ -14049,8 +14055,6 @@ namespace BinaryNinja { protected: bool m_queryMode = false; - FlowGraph(BNFlowGraph* graph); - void FinishPrepareForLayout(); virtual void PrepareForLayout(); virtual void PopulateNodes(); @@ -14058,6 +14062,7 @@ namespace BinaryNinja { public: FlowGraph(); + FlowGraph(BNFlowGraph* graph); /*! Get the Function associated with this FlowGraph @@ -16664,6 +16669,730 @@ namespace BinaryNinja { std::optional GetDerivedStringReferenceForExpr(size_t expr); }; + class SimilarityProviderType; + + /*! Identifies an entity within a similarity-session node. */ + using SimilarityEntityId = + bn::base::StrongTypedef, st::Ordered, st::Hashable, + st::Incrementable>; + /*! Identifies a provider result within a similarity-session node. */ + using SimilarityResultId = + bn::base::StrongTypedef, st::Ordered, st::Hashable, + st::Incrementable>; + /*! Identifies a node across similarity sessions. */ + using SimilaritySessionNodeId = + bn::base::StrongTypedef, st::Ordered, + st::Hashable>; + /*! Identifies a similarity session. */ + using SimilaritySessionId = + bn::base::StrongTypedef, st::Ordered, st::Hashable>; + /*! Identifies a provider instance. */ + using SimilarityProviderId = + bn::base::StrongTypedef, st::Ordered, st::Hashable>; + /*! Identifies a resolver instance. */ + using SimilaritySessionResolverId = + bn::base::StrongTypedef, st::Ordered, + st::Hashable>; + + /*! Chooses which session completion data to read or update. + + A query cannot select both a provider and a resolver. Omitting all IDs selects the whole session. */ + struct SimilaritySessionCompletionQuery + { + std::optional nodeId; + std::optional providerId; + std::optional resolverId; + + static SimilaritySessionCompletionQuery ForSession() { return {}; } + static SimilaritySessionCompletionQuery ForNode(SimilaritySessionNodeId node) { return {.nodeId = node}; } + static SimilaritySessionCompletionQuery ForProvider(SimilarityProviderId provider) + { + return {.providerId = provider}; + } + static SimilaritySessionCompletionQuery ForResolver(SimilaritySessionResolverId resolver) + { + return {.resolverId = resolver}; + } + SimilaritySessionCompletionQuery WithProvider(SimilarityProviderId provider) const + { + return {.nodeId = nodeId, .providerId = provider}; + } + SimilaritySessionCompletionQuery WithResolver(SimilaritySessionResolverId resolver) const + { + return {.nodeId = nodeId, .resolverId = resolver}; + } + + [[nodiscard]] BNSimilaritySessionCompletionQuery ToRaw() const + { + return {nodeId.has_value(), nodeId.value_or(SimilaritySessionNodeId(0)), providerId.has_value(), + providerId.value_or(SimilarityProviderId(0)), resolverId.has_value(), + resolverId.value_or(SimilaritySessionResolverId(0))}; + } + }; + + /*! Identifies an entity within a session node. */ + struct SimilarityEntityRef + { + SimilaritySessionNodeId nodeId; + SimilarityEntityId entityId; + + SimilarityEntityRef(SimilaritySessionNodeId nodeId, SimilarityEntityId entityId) : + nodeId(nodeId), entityId(entityId) + {} + SimilarityEntityRef(const BNSimilarityEntityRef& ref) : nodeId(ref.nodeId), entityId(ref.entityId) {} + bool operator==(const SimilarityEntityRef& other) const = default; + bool operator<(const SimilarityEntityRef& other) const + { + return std::tie(nodeId, entityId) < std::tie(other.nodeId, other.entityId); + } + + [[nodiscard]] BNSimilarityEntityRef ToRaw() const + { + return {nodeId, entityId}; + } + }; + + /*! A match produced by a provider. + + Similarity and confidence range from 0 to 255, where 255 is strongest. Automatic metadata transfer requires + `target` to identify an active function. */ + struct SimilarityResult + { + /*! The provider which produced the match. */ + SimilarityProviderId providerId; + /*! The similarity of the two entities. */ + uint8_t similarity; + /*! The provider's confidence in the match. */ + uint8_t confidence; + /*! The matched entity, which may be used as the source for metadata transfer. */ + SimilarityEntityRef target; + + SimilarityResult(SimilarityProviderId provider, uint8_t similarity, uint8_t confidence, + const SimilarityEntityRef& target) : + providerId(provider), similarity(similarity), confidence(confidence), target(target) + {} + SimilarityResult(const BNSimilarityResult& result) : + providerId(result.providerId), similarity(result.similarity), confidence(result.confidence), + target(result.target) + {} + bool operator==(const SimilarityResult& other) const = default; + + [[nodiscard]] BNSimilarityResult ToRaw() const + { + return {providerId, similarity, confidence, target.ToRaw()}; + } + }; + + /*! Describes an entity, including its display name. */ + struct SimilarityEntityInfo + { + BNSimilarityEntityType type; + uint64_t address; + std::string name; + + SimilarityEntityInfo(BNSimilarityEntityType type, uint64_t address, std::string name = {}) : + type(type), address(address), name(std::move(name)) + {} + SimilarityEntityInfo(const BNSimilarityEntityInfo& info) : + type(info.type), address(info.address), name(info.name ? info.name : "") + {} + bool operator==(const SimilarityEntityInfo& other) const = default; + + [[nodiscard]] BNSimilarityEntityInfo ToRaw() const { return {type, address, name.c_str()}; } + }; + + /*! Applies a diff annotation to the half-open address range `[start, end)`. */ + struct SimilarityRangeAnnotation + { + uint64_t start; + uint64_t end; + BNSimilarityAnnotationType type; + + SimilarityRangeAnnotation(uint64_t start, uint64_t end, BNSimilarityAnnotationType type) : + start(start), end(end), type(type) + {} + SimilarityRangeAnnotation(const BNSimilarityRangeAnnotation& annotation) : + start(annotation.start), end(annotation.end), type(annotation.type) + {} + [[nodiscard]] BNSimilarityRangeAnnotation ToRaw() const { return {start, end, type}; } + }; + + /*! A flow-graph or linear view produced while rendering a similarity result. */ + class SimilarityView : + public CoreRefCountObject + { + public: + SimilarityView(BNSimilarityView* view); + + std::string GetGroup() const; + BNSimilarityViewType GetType() const; + /*! Returns the flow graph, or `nullptr` for a linear view. */ + Ref GetFlowGraph() const; + /*! Returns the data view, or `nullptr` for a flow graph. */ + Ref GetLinearViewData() const; + /*! Returns the linear view, or `nullptr` for a flow graph. */ + Ref GetLinearView() const; + /*! Returns the session entity for this view, if its renderer provided one. */ + std::optional GetEntity() const; + }; + + /*! Holds the grouped views used to display a similarity result. */ + class SimilarityRenderContext : + public CoreRefCountObject + { + public: + SimilarityRenderContext(); + SimilarityRenderContext(BNSimilarityRenderContext* context); + + /*! Sets the function representation preferred by renderers writing to this context. */ + void SetPreferredViewType(const FunctionViewType& type); + /*! Returns the function representation preferred by renderers writing to this context. */ + FunctionViewType GetPreferredViewType() const; + /*! Adds a flow graph to a view group. */ + void AddFlowGraph(const std::string& group, FlowGraph& graph); + /*! Adds a flow graph for a session entity to a view group. */ + void AddFlowGraph(const std::string& group, FlowGraph& graph, const SimilarityEntityRef& entity); + /*! Adds a linear view to a view group. */ + void AddLinearView(const std::string& group, BinaryView& data, LinearViewObject& linearView); + /*! Adds a linear view for a session entity to a view group. */ + void AddLinearView(const std::string& group, BinaryView& data, LinearViewObject& linearView, + const SimilarityEntityRef& entity); + /*! Returns views in insertion order. */ + std::vector> GetViews() const; + }; + + /*! Highlights annotated address ranges in flow-graph and linear views. */ + class DiffRenderer : public CoreRefCountObject + { + public: + DiffRenderer(); + DiffRenderer(BNDiffRenderer* renderer); + + /*! Adds a half-open address range to annotate. + + \note Empty ranges are ignored. */ + void AddRangeAnnotation(const SimilarityRangeAnnotation& annotation); + void AddRangeAnnotation(uint64_t start, uint64_t end, BNSimilarityAnnotationType type); + + /*! Renders graph and linear views for a function. */ + void Render(SimilarityRenderContext& context, Function& function); + /*! Renders graph and linear views for a function and session entity. */ + void Render(SimilarityRenderContext& context, Function& function, const SimilarityEntityRef& entity); + /*! Renders an annotated flow graph. */ + void Render(SimilarityRenderContext& context, const std::string& group, FlowGraph& graph); + /*! Renders an annotated flow graph for a session entity. */ + void Render(SimilarityRenderContext& context, const std::string& group, FlowGraph& graph, + const SimilarityEntityRef& entity); + /*! Renders an annotated linear view. */ + void Render( + SimilarityRenderContext& context, const std::string& group, BinaryView& data, LinearViewObject& linearView); + /*! Renders an annotated linear view for a session entity. */ + void Render(SimilarityRenderContext& context, const std::string& group, BinaryView& data, + LinearViewObject& linearView, const SimilarityEntityRef& entity); + }; + + class SimilaritySessionNode; + class SimilaritySessionCompletion; + + /*! Holds the provider results produced by a node or edge visit. + * + * Only use an instance during the provider callback that received it. A successful visit replaces earlier results + * for the same provider and node or edge. Results for unscheduled entities remain unchanged. */ + class SimilarityProviderResults + { + BNSimilarityProviderResults* m_object; + + public: + explicit SimilarityProviderResults(BNSimilarityProviderResults* results) : m_object(results) {} + + BNSimilarityProviderResults* GetObject() const { return m_object; } + + /*! Adds a result to the current visit. + * + * The result must belong to an entity scheduled for this visit. Node results belong to `source`; edge results + * belong to the entity on the destination node. Returns zero if the result could not be added. A later visit + * replaces the result and gives it a new ID. */ + SimilarityResultId AddResult(const SimilarityEntityRef& source, const SimilarityEntityRef& target, + uint8_t similarity, uint8_t confidence); + }; + + /*! Produces, applies, and renders similarity results for session entities. + * C++ implementations must be thread safe because callbacks may overlap across nodes and sessions. Provider visits + * must write changes through the given SimilarityProviderResults. Visits made by a session keep the visited node and + * both edge endpoints active. Direct calls must provide active views. */ + class SimilarityProvider : + public CoreRefCountObject + { + static bool UpdateSettingsCallback(void* ctxt, BNSettings* settings); + static bool VisitNodeCallback(void* ctxt, BNSimilaritySessionNode* node, BNSimilarityProviderResults* results, + BNSimilaritySessionCompletion* completion); + static bool VisitNodeEdgeCallback(void* ctxt, BNSimilaritySessionNode* from, BNSimilaritySessionNode* to, + BNSimilarityProviderResults* results, BNSimilaritySessionCompletion* completion); + static char* GetNameCallback( + void* ctxt, BNSimilaritySessionNode* node, BNSimilarityEntityId entity, BNSimilarityResultId result); + static BNSimilarityApplyStatus ApplyCallback( + void* ctxt, BNSimilaritySessionNode* node, BNSimilarityEntityId entity, BNSimilarityResultId result); + static void RenderCallback(void* ctxt, BNSimilaritySessionNode* node, BNSimilarityEntityId entity, + BNSimilarityRenderContext* context, BNSimilarityResultId result); + static void FreeContextCallback(void* ctxt); + + public: + SimilarityProvider(SimilarityProviderType* type); + SimilarityProvider(BNSimilarityProvider* provider); + + Ref GetType() const; + SimilarityProviderId GetId() const; + + /*! Replaces this provider's settings only if they are valid. + + Return false without changing the current settings when the new settings are invalid or updates are not + supported. Use SimilaritySession::UpdateProviderSettings so affected entities are scheduled again. */ + virtual bool UpdateSettings(Settings&) { return false; } + + /*! Performs a complete node visit. The core manages the result updates. */ + void VisitNode(SimilaritySessionNode& node, SimilaritySessionCompletion& completion); + /*! Performs a complete edge visit. The core manages the result updates. */ + void VisitNodeEdge( + SimilaritySessionNode& from, SimilaritySessionNode& to, SimilaritySessionCompletion& completion); + + /*! Visits a node and writes results for it. Return `false` to discard the visit. */ + virtual bool VisitNode(SimilaritySessionNode&, SimilarityProviderResults&, SimilaritySessionCompletion&) + { + return true; + } + + /*! Visits an edge after both endpoint nodes have been visited and writes results for the edge. + * Return `false` to discard the visit. */ + virtual bool VisitNodeEdge( + SimilaritySessionNode&, SimilaritySessionNode&, SimilarityProviderResults&, SimilaritySessionCompletion&) + { + return true; + } + + /*! Returns the display name for a result, if available. */ + virtual std::optional GetName(SimilaritySessionNode& node, + SimilarityEntityId entity, SimilarityResultId result) = 0; + + /*! Applies a result. The default implementation transfers metadata from the result target; overrides can call it + before adding provider-specific metadata. */ + virtual BNSimilarityApplyStatus Apply(SimilaritySessionNode& node, SimilarityEntityId entity, + SimilarityResultId result); + + /*! Adds views for a result to `context`. */ + virtual void Render(SimilaritySessionNode& node, SimilarityEntityId entity, + SimilarityRenderContext& context, SimilarityResultId result) = 0; + }; + + class CoreSimilarityProvider : public SimilarityProvider + { + public: + CoreSimilarityProvider(BNSimilarityProvider* provider); + + using SimilarityProvider::VisitNode; + using SimilarityProvider::VisitNodeEdge; + + bool VisitNode(SimilaritySessionNode& node, SimilarityProviderResults& results, + SimilaritySessionCompletion& completion) override; + bool VisitNodeEdge(SimilaritySessionNode& from, SimilaritySessionNode& to, SimilarityProviderResults& results, + SimilaritySessionCompletion& completion) override; + + std::optional GetName(SimilaritySessionNode& node, + SimilarityEntityId entity, SimilarityResultId result) override; + + BNSimilarityApplyStatus Apply( + SimilaritySessionNode& node, SimilarityEntityId entity, SimilarityResultId result) override; + + void Render(SimilaritySessionNode& node, SimilarityEntityId entity, + SimilarityRenderContext& context, SimilarityResultId result) override; + }; + + /*! Creates similarity providers with the given settings. */ + class SimilarityProviderType : public StaticCoreRefCountObject + { + std::string m_nameForRegister; + std::string m_descForRegister; + + static BNSimilarityProvider* CreateCallback(void* ctxt, BNSettings* settings); + static BNSettings* GetDefaultSettingsCallback(void* ctxt); + + public: + SimilarityProviderType(std::string name, std::string description); + SimilarityProviderType(BNSimilarityProviderType* formatter); + + /*! Registers a provider type for the lifetime of the process. */ + static void Register(SimilarityProviderType* type); + + static std::vector> GetList(); + static Ref GetByName(const std::string& name); + + std::string GetName() const; + std::string GetDescription() const; + + /*! Creates a provider with the given settings, or returns null. */ + virtual Ref Create(Settings& settings) = 0; + + /*! Returns the settings schema and defaults for this provider type, or null. */ + virtual Ref GetDefaultSettings() = 0; + }; + + class CoreSimilarityProviderType : public SimilarityProviderType + { + public: + CoreSimilarityProviderType(BNSimilarityProviderType* type); + + /*! Returns null outside the Ultimate edition. */ + Ref Create(Settings& settings) override; + + Ref GetDefaultSettings() override; + }; + + class SimilaritySession; + class SimilaritySessionReceiver; + class SimilaritySessionGraphReceiver; + class SimilaritySessionResolverType; + + /*! Selects a preferred result for each scheduled entity. + * C++ implementations must be thread safe because callbacks may overlap across nodes in the same processing group. */ + class SimilaritySessionResolver : + public CoreRefCountObject + { + static bool UpdateSettingsCallback(void* ctxt, BNSettings* settings); + static void PrepareForNodeCallback(void* ctxt, BNSimilaritySession* session, BNSimilaritySessionNode* node, + BNSimilaritySessionCompletion* completion, BNSimilaritySessionResolverId resolverId); + static void ResolveForNodeCallback(void* ctxt, BNSimilaritySession* session, BNSimilaritySessionNode* node, + const BNSimilarityEntityId* entities, size_t entityCount, BNSimilaritySessionCompletion* completion, + BNSimilaritySessionResolverId resolverId); + static void FreeContextCallback(void* ctxt); + + public: + /*! Creates a resolver for `session`. */ + SimilaritySessionResolver(SimilaritySessionResolverType* type, Ref session); + SimilaritySessionResolver(BNSimilaritySessionResolver* resolver); + + SimilaritySessionResolverId GetId() const; + Ref GetType() const; + + /*! Replaces this resolver's settings only if they are valid. + + Return false without changing the current settings when the new settings are invalid or updates are not + supported. Use SimilaritySession::UpdateResolverSettings so affected entities are resolved again. */ + virtual bool UpdateSettings(Settings&) { return false; } + + /*! Prepares a node before providers run. + + This is useful for large graphs where views may be unavailable, but the resolver needs to change the scheduled + entities or add entities itself. */ + virtual void PrepareForNode(SimilaritySession& session, SimilaritySessionNode& node, + SimilaritySessionCompletion& completion) {} + /*! Resolves provider results after all providers have visited the node. + + Call SetResolvedResult to select a result. Call AddScheduledEntity to request another provider and resolver + round for an entity. Only schedule work for `node`; the session owns scheduling between nodes. */ + virtual void ResolveForNode(SimilaritySession& session, SimilaritySessionNode& node, + const std::vector& entities, SimilaritySessionCompletion& completion) = 0; + }; + + class CoreSimilaritySessionResolver : public SimilaritySessionResolver + { + public: + CoreSimilaritySessionResolver(BNSimilaritySessionResolver* resolver); + + void PrepareForNode(SimilaritySession& session, SimilaritySessionNode& node, + SimilaritySessionCompletion& completion) override; + void ResolveForNode(SimilaritySession& session, SimilaritySessionNode& node, + const std::vector& entities, SimilaritySessionCompletion& completion) override; + }; + + /*! Creates resolvers with the given settings. */ + class SimilaritySessionResolverType : public StaticCoreRefCountObject + { + std::string m_nameForRegister; + std::string m_descForRegister; + + static BNSimilaritySessionResolver* CreateCallback( + void* ctxt, BNSimilaritySession* session, BNSettings* settings); + static BNSettings* GetDefaultSettingsCallback(void* ctxt); + + public: + SimilaritySessionResolverType(std::string name, std::string description); + SimilaritySessionResolverType(BNSimilaritySessionResolverType* type); + + /*! Registers a resolver type for the lifetime of the process. */ + static void Register(SimilaritySessionResolverType* type); + static Ref GetByName(const std::string& name); + static std::vector> GetList(); + + std::string GetName() const; + std::string GetDescription() const; + + /*! Creates a resolver for `session`, or returns null. The resolver must not keep `session` after this call. */ + virtual Ref Create(Ref session, Settings& settings) = 0; + /*! Returns the settings schema and defaults for this resolver type, or null. */ + virtual Ref GetDefaultSettings() = 0; + }; + + class CoreSimilaritySessionResolverType : public SimilaritySessionResolverType + { + public: + CoreSimilaritySessionResolverType(BNSimilaritySessionResolverType* type); + + Ref Create(Ref session, Settings& settings) override; + Ref GetDefaultSettings() override; + }; + + /*! The main unit of similarity processing. */ + class SimilaritySessionNode : + public CoreRefCountObject + { + public: + SimilaritySessionNode(BNSimilaritySessionNode* node); + /*! Creates a node for a non-null active view, keeps the view alive, and schedules its analyzed functions. */ + SimilaritySessionNode(Ref view); + /*! Creates a node whose view will be loaded from `file` when the session runs. + + `file` must be non-null. The session closes the view when the run no longer needs it, so the view may be + unavailable at other times. */ + SimilaritySessionNode(Ref file); + + /*! Returns the active view, or `nullptr` if the node is not loaded. + + A file-backed node may be unavailable outside a session run. */ + Ref GetView() const; + /*! Sets the active view. A view backed by a different FileMetadata is ignored. + + \note Do not mix files. */ + void SetView(Ref view); + /*! Returns the file used by the node. This is always valid. */ + Ref GetFile() const; + /*! Returns mutable settings used when the session loads this node's view. Modify them before running the session. */ + Ref GetLoadOptions() const; + SimilaritySessionNodeId GetId() const; + + /*! Adds an entity without scheduling it. Existing entities are reused, and a non-empty + name refreshes their display name. */ + SimilarityEntityId CreateEntity(const SimilarityEntityInfo& info); + /*! Removes an entity, its schedule, its provider results, and its selected result. + + To only unschedule it, call RemoveScheduledEntity. */ + bool RemoveEntity(SimilarityEntityId id); + /*! Returns information about an entity, or no value if `id` is not found. */ + std::optional GetEntity(SimilarityEntityId id); + /*! Returns every entity in the node, including entities used only as match targets. */ + std::vector GetEntities(); + /*! Schedules an entity for the next provider round. + + Nodes initially schedule all available entities. The session consumes each scheduled batch after providers + visit it. During a run, only the resolver currently processing this node may request another round. Schedule + entities from other contexts between runs. */ + bool AddScheduledEntity(SimilarityEntityId id); + /*! Unschedules an entity without removing it from the node. + + To remove it, call RemoveEntity. */ + bool RemoveScheduledEntity(SimilarityEntityId id); + /*! Returns the entities waiting for provider processing. */ + std::vector GetScheduledEntities(); + /*! Resolves a function entity against the active view, or returns `nullptr`. */ + Ref GetEntityFunction(SimilarityEntityId id); + /*! Returns the result IDs for an entity. */ + std::vector GetResults(SimilarityEntityId entity); + /*! Returns a stored result by its ID, which is unique within the node. */ + std::optional GetResult(SimilarityResultId result); + /*! Selects one of the entity's results. */ + bool SetResolvedResult(SimilarityEntityId entity, SimilarityResultId result); + std::optional GetResolvedResult(SimilarityEntityId entity); + bool ClearResolvedResult(SimilarityEntityId entity); + + /*! Returns incoming node IDs in ascending order. */ + std::vector GetIncomingEdges(); + /*! Returns outgoing node IDs in ascending order. */ + std::vector GetOutgoingEdges(); + /*! Returns incoming nodes ordered by ID. */ + std::vector> GetIncomingNodes(); + /*! Returns outgoing nodes ordered by ID. */ + std::vector> GetOutgoingNodes(); + }; + + /*! A graph that controls which binaries are compared and in what order. The graph cannot contain cycles. + + An edge from A to B makes A available as an incoming comparison node while B is processed. */ + class SimilaritySessionGraph : + public CoreRefCountObject + { + public: + SimilaritySessionGraph(BNSimilaritySessionGraph* graph); + + /*! Adds a node, moving it from its previous graph if necessary. If either graph is running, the node is unchanged. */ + void AddNode(Ref node); + /*! Removes a node and its incident edges. Graph mutations are ignored during a run. */ + void RemoveNode(SimilaritySessionNode& node); + /*! Returns a node, or `nullptr` if `id` is absent. */ + Ref GetNode(SimilaritySessionNodeId id); + std::vector> GetNodes(); + + /*! Returns whether an edge joins two graph members without duplicating an edge or creating a cycle. + * Returns false during a run. */ + bool IsValidEdge(SimilaritySessionNode& from, SimilaritySessionNode& to); + /*! Adds an edge. Returns false if invalid or while the graph is running. */ + bool AddEdge(SimilaritySessionNode& from, SimilaritySessionNode& to); + /*! Removes an edge. Returns false if absent or while the graph is running. */ + bool RemoveEdge(SimilaritySessionNode& from, SimilaritySessionNode& to); + void AddReceiver(Ref receiver); + void RemoveReceiver(SimilaritySessionGraphReceiver& receiver); + std::vector> GetReceivers(); + + /*! Returns groups of nodes in processing order. Nodes in the same group may run concurrently. */ + std::vector>> GetSchedule(); + }; + + /*! Receives notifications after nodes or edges are added to or removed from a session graph. */ + class SimilaritySessionGraphReceiver : + public CoreRefCountObject + { + static void OnGraphChangedCallback(void* ctxt); + static void FreeContextCallback(void* ctxt); + + public: + SimilaritySessionGraphReceiver(); + SimilaritySessionGraphReceiver(BNSimilaritySessionGraphReceiver* receiver); + + virtual void NotifyGraphChanged() {} + }; + + class CoreSimilaritySessionGraphReceiver : public SimilaritySessionGraphReceiver + { + public: + CoreSimilaritySessionGraphReceiver(BNSimilaritySessionGraphReceiver* receiver); + + void NotifyGraphChanged() override; + }; + + /*! Receives session-start and entity-batch notifications. + * C++ implementations must be thread safe. NotifyStart is called before Run returns. NotifyBatch runs on workers + * and may overlap across nodes and sessions. Receivers are observers and must not + * schedule work or mutate the active session. */ + class SimilaritySessionReceiver : + public CoreRefCountObject + { + static void OnStartedCallback(void* ctxt, BNSimilaritySessionCompletion* completion); + static void OnUpdatedCallback(void* ctxt, BNSimilaritySessionNode* node, BNSimilarityProvider* provider, + const BNSimilarityEntityId* entities, size_t count); + static void FreeContextCallback(void* ctxt); + + public: + SimilaritySessionReceiver(); + SimilaritySessionReceiver(BNSimilaritySessionReceiver* receiver); + + /*! Called when the session starts a run. + + This is mainly used to get the completion state for the run. */ + virtual void NotifyStart(SimilaritySessionCompletion& completion) {} + /*! Called when a provider's results, resolution state, or applied metadata changes for a batch of entities. */ + virtual void NotifyBatch(SimilaritySessionNode& node, SimilarityProvider& provider, + const std::vector& entities) + {} + }; + + class CoreSimilaritySessionReceiver : public SimilaritySessionReceiver + { + public: + CoreSimilaritySessionReceiver(BNSimilaritySessionReceiver* receiver); + + void NotifyStart(SimilaritySessionCompletion& completion) override; + void NotifyBatch(SimilaritySessionNode& node, SimilarityProvider& provider, + const std::vector& entities) override; + }; + + /*! Tracks stop requests, progress, and timing for a session run. */ + class SimilaritySessionCompletion : + public CoreRefCountObject + { + public: + SimilaritySessionCompletion(BNSimilaritySessionCompletion* completion); + /*! Creates an independent completion state, normally only for calling providers or resolvers directly. */ + SimilaritySessionCompletion(); + + bool IsFinished() const; + /*! Asks the run to stop. Long-running callbacks should check IsStopRequested regularly. */ + void RequestStop(); + bool IsStopRequested() const; + /*! Returns progress from 0.0 to 1.0. Exactly 1.0 means the selected part of the run has finished. */ + double GetProgress(const SimilaritySessionCompletionQuery& query) const; + /*! Increases progress for a node and one provider or resolver. Progress cannot decrease. + + \note Call this only from the provider or resolver selected by `query`. */ + void SetProgress(const SimilaritySessionCompletionQuery& query, double progress); + /*! Returns elapsed time for the selected part of the run. */ + std::chrono::milliseconds GetTiming(const SimilaritySessionCompletionQuery& query) const; + }; + + /*! Runs providers and resolvers over binaries arranged in a session graph. */ + class SimilaritySession : + public CoreRefCountObject + { + public: + SimilaritySession(BNSimilaritySession* session); + SimilaritySession(); + + SimilaritySessionId GetId() const; + + /*! Adds a provider and schedules entities processed by earlier runs for the next run. + + \note Ignored while a run is active. */ + void AddProvider(Ref provider); + /*! Removes a provider, clears its results, and marks affected entities for resolution. + + \note Ignored while a run is active. */ + void RemoveProvider(SimilarityProvider& provider); + /*! Updates a provider already in the session and schedules previously processed entities again. + + Returns false during a run, when the provider is absent, or when it rejects the settings. */ + bool UpdateProviderSettings(SimilarityProvider& provider, Settings& settings); + /*! Returns a provider, or `nullptr` if `id` is absent. */ + Ref GetProvider(SimilarityProviderId id); + std::vector> GetProviders(); + + /*! Adds a resolver created for this session and marks entities processed by earlier runs for resolution. + + \note Returns false during a run, for a duplicate, or for a resolver from another session. */ + bool AddResolver(Ref resolver); + /*! Removes a resolver. + + \note Returns false during a run, or if it is absent or belongs to another session. */ + bool RemoveResolver(SimilaritySessionResolver& resolver); + /*! Updates a resolver already in the session and marks previously processed entities for resolution. + + Returns false during a run, when the resolver is absent or belongs to another session, or when it rejects the + settings. */ + bool UpdateResolverSettings(SimilaritySessionResolver& resolver, Settings& settings); + /*! Returns a resolver, or `nullptr` if `id` is absent. */ + Ref GetResolver(SimilaritySessionResolverId id); + std::vector> GetResolvers(); + /*! Adds a receiver. A running session keeps using the receiver list it started with. */ + void AddReceiver(Ref receiver); + /*! Removes a receiver. A running session keeps using the receiver list it started with. */ + void RemoveReceiver(SimilaritySessionReceiver& receiver); + std::vector> GetReceivers(); + + Ref GetGraph(); + /*! Starts a background run with the current graph, providers, and resolvers. Changes are ignored until it finishes. + + \note Returns the active run's completion handle when already running. */ + Ref Run(); + }; + struct LineFormatterSettings { Ref highLevelIL; @@ -24461,6 +25190,17 @@ namespace std return std::hash()(value.operator std::string_view()); } }; + + template <> + struct hash + { + size_t operator()(BinaryNinja::SimilarityEntityRef const& value) const + { + const size_t nodeHash = std::hash()(value.nodeId); + const size_t entityHash = std::hash()(value.entityId); + return nodeHash ^ (entityHash + 0x9e3779b9 + (nodeHash << 6) + (nodeHash >> 2)); + } + }; } // namespace std diff --git a/binaryninjacore.h b/binaryninjacore.h index b1e83870ac..925c49968f 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 179 +#define BN_CURRENT_CORE_ABI_VERSION 180 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 179 +#define BN_MINIMUM_CORE_ABI_VERSION 180 #define BN_DEMANGLER_MSVC "MS" #define BN_DEMANGLER_GNU3 "GNU3" @@ -359,6 +359,20 @@ extern "C" typedef struct BNConstantRenderer BNConstantRenderer; typedef struct BNStringRecognizer BNStringRecognizer; typedef struct BNCustomStringType BNCustomStringType; + typedef struct BNSimilarityProviderType BNSimilarityProviderType; + typedef struct BNSimilarityProvider BNSimilarityProvider; + typedef struct BNSimilarityProviderResults BNSimilarityProviderResults; + typedef struct BNSimilarityRenderContext BNSimilarityRenderContext; + typedef struct BNSimilarityView BNSimilarityView; + typedef struct BNDiffRenderer BNDiffRenderer; + typedef struct BNSimilaritySessionResolverType BNSimilaritySessionResolverType; + typedef struct BNSimilaritySessionResolver BNSimilaritySessionResolver; + typedef struct BNSimilaritySessionNode BNSimilaritySessionNode; + typedef struct BNSimilaritySessionGraph BNSimilaritySessionGraph; + typedef struct BNSimilaritySessionGraphReceiver BNSimilaritySessionGraphReceiver; + typedef struct BNSimilaritySessionCompletion BNSimilaritySessionCompletion; + typedef struct BNSimilaritySessionReceiver BNSimilaritySessionReceiver; + typedef struct BNSimilaritySession BNSimilaritySession; typedef struct BNVersionInfo { uint32_t major; @@ -4300,6 +4314,423 @@ extern "C" char* stringPostfix; } BNCustomStringTypeInfo; + typedef struct BNSimilarityEntityId + { + uint32_t value; + } BNSimilarityEntityId; + + typedef struct BNSimilarityResultId + { + uint64_t value; + } BNSimilarityResultId; + + typedef struct BNSimilaritySessionNodeId + { + uint32_t value; + } BNSimilaritySessionNodeId; + + typedef struct BNSimilaritySessionId + { + uint32_t value; + } BNSimilaritySessionId; + + typedef struct BNSimilarityProviderId + { + uint32_t value; + } BNSimilarityProviderId; + + typedef struct BNSimilaritySessionResolverId + { + uint32_t value; + } BNSimilaritySessionResolverId; + + typedef struct BNSimilarityEntityRef + { + BNSimilaritySessionNodeId nodeId; + BNSimilarityEntityId entityId; + } BNSimilarityEntityRef; + + BN_ENUM(uint8_t, BNSimilarityEntityType) + { + SimilarityEntityFunction = 0, + }; + + BN_ENUM(uint8_t, BNSimilarityApplyStatus) + { + SimilarityApplySuccess = 0, + SimilarityApplyNodeInactive = 1, + SimilarityApplyEntityNotFound = 2, + SimilarityApplyUnsupported = 3, + SimilarityApplyFailed = 4, + }; + + typedef struct BNSimilarityResult + { + BNSimilarityProviderId providerId; + uint8_t similarity; + uint8_t confidence; + BNSimilarityEntityRef target; + } BNSimilarityResult; + + typedef struct BNSimilarityEntityInfo + { + BNSimilarityEntityType type; + uint64_t address; + const char* name; + } BNSimilarityEntityInfo; + + BN_ENUM(uint8_t, BNSimilarityViewType) { + SimilarityViewFlowGraph = 0, + SimilarityViewLinear = 1, + }; + + BN_ENUM(uint8_t, BNSimilarityAnnotationType) { + SimilarityAnnotationAdded = 0, + SimilarityAnnotationRemoved = 1, + SimilarityAnnotationChanged = 2, + }; + + typedef struct BNSimilarityRangeAnnotation + { + uint64_t start; + uint64_t end; + BNSimilarityAnnotationType type; + } BNSimilarityRangeAnnotation; + + typedef struct BNSimilaritySessionCompletionQuery + { + bool hasNodeId; + BNSimilaritySessionNodeId nodeId; + bool hasProviderId; + BNSimilarityProviderId providerId; + bool hasResolverId; + BNSimilaritySessionResolverId resolverId; + } BNSimilaritySessionCompletionQuery; + + typedef struct BNCustomSimilarityProvider + { + void* context; + void (*externalRefTaken)(void* ctxt); + void (*externalRefReleased)(void* ctxt); + bool (*updateSettings)(void* ctxt, BNSettings* settings); + bool (*visitNode)(void* ctxt, BNSimilaritySessionNode* node, BNSimilarityProviderResults* results, + BNSimilaritySessionCompletion* completion); + bool (*visitNodeEdge)(void* ctxt, BNSimilaritySessionNode* from, BNSimilaritySessionNode* to, + BNSimilarityProviderResults* results, BNSimilaritySessionCompletion* completion); + char* (*getName)(void* ctxt, BNSimilaritySessionNode* node, BNSimilarityEntityId entity, + BNSimilarityResultId result); + BNSimilarityApplyStatus (*apply)(void* ctxt, BNSimilaritySessionNode* node, BNSimilarityEntityId entity, + BNSimilarityResultId result); + void (*render)(void* ctxt, BNSimilaritySessionNode* node, BNSimilarityEntityId entity, + BNSimilarityRenderContext* context, BNSimilarityResultId result); + void (*free)(void* ctxt); + } BNCustomSimilarityProvider; + + typedef struct BNCustomSimilarityProviderType + { + void* context; + BNSimilarityProvider* (*create)(void* ctxt, BNSettings* settings); + BNSettings* (*getDefaultSettings)(void* ctxt); + } BNCustomSimilarityProviderType; + + typedef struct BNCustomSimilaritySessionResolver + { + void* context; + void (*externalRefTaken)(void* ctxt); + void (*externalRefReleased)(void* ctxt); + bool (*updateSettings)(void* ctxt, BNSettings* settings); + void (*prepareForNode)(void* ctxt, BNSimilaritySession* session, BNSimilaritySessionNode* node, + BNSimilaritySessionCompletion* completion, BNSimilaritySessionResolverId resolverId); + void (*resolveForNode)(void* ctxt, BNSimilaritySession* session, BNSimilaritySessionNode* node, + const BNSimilarityEntityId* entities, size_t entityCount, BNSimilaritySessionCompletion* completion, + BNSimilaritySessionResolverId resolverId); + void (*free)(void* ctxt); + } BNCustomSimilaritySessionResolver; + + typedef struct BNCustomSimilaritySessionResolverType + { + void* context; + BNSimilaritySessionResolver* (*create)(void* ctxt, BNSimilaritySession* session, BNSettings* settings); + BNSettings* (*getDefaultSettings)(void* ctxt); + } BNCustomSimilaritySessionResolverType; + + typedef struct BNCustomSimilaritySessionReceiver + { + void* context; + void (*externalRefTaken)(void* ctxt); + void (*externalRefReleased)(void* ctxt); + void (*onStarted)(void* ctxt, BNSimilaritySessionCompletion* completion); + void (*onUpdated)(void* ctxt, BNSimilaritySessionNode* node, BNSimilarityProvider* provider, + const BNSimilarityEntityId* entities, size_t count); + void (*free)(void* ctxt); + } BNCustomSimilaritySessionReceiver; + + typedef struct BNCustomSimilaritySessionGraphReceiver + { + void* context; + void (*externalRefTaken)(void* ctxt); + void (*externalRefReleased)(void* ctxt); + void (*onGraphChanged)(void* ctxt); + void (*free)(void* ctxt); + } BNCustomSimilaritySessionGraphReceiver; + + BINARYNINJACOREAPI BNSimilarityProviderType* BNRegisterSimilarityProviderType( + const char* name, const char* description, BNCustomSimilarityProviderType* type); + BINARYNINJACOREAPI BNSimilarityProviderType* BNGetSimilarityProviderTypeByName(const char* name); + BINARYNINJACOREAPI BNSimilarityProviderType** BNGetSimilarityProviderTypeList(size_t* count); + BINARYNINJACOREAPI void BNFreeSimilarityProviderTypeList(BNSimilarityProviderType** types); + BINARYNINJACOREAPI char* BNSimilarityProviderTypeGetName(BNSimilarityProviderType* type); + BINARYNINJACOREAPI char* BNSimilarityProviderTypeGetDescription(BNSimilarityProviderType* type); + /*! Returns `nullptr` outside the Ultimate edition. */ + BINARYNINJACOREAPI BNSimilarityProvider* BNSimilarityProviderTypeCreateProvider( + BNSimilarityProviderType* type, BNSettings* settings); + BINARYNINJACOREAPI BNSettings* BNSimilarityProviderTypeGetDefaultSettings(BNSimilarityProviderType* type); + + BINARYNINJACOREAPI BNSimilarityProvider* BNCreateCustomSimilarityProvider( + BNSimilarityProviderType* type, BNCustomSimilarityProvider* callbacks); + BINARYNINJACOREAPI void BNSimilarityProviderVisitNode( + BNSimilarityProvider* provider, BNSimilaritySessionNode* node, BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI void BNSimilarityProviderVisitNodeEdge(BNSimilarityProvider* provider, + BNSimilaritySessionNode* from, BNSimilaritySessionNode* to, BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI bool BNSimilarityProviderPerformVisitNode(BNSimilarityProvider* provider, + BNSimilaritySessionNode* node, BNSimilarityProviderResults* results, BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI bool BNSimilarityProviderPerformVisitNodeEdge(BNSimilarityProvider* provider, + BNSimilaritySessionNode* from, BNSimilaritySessionNode* to, BNSimilarityProviderResults* results, + BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI BNSimilarityResultId BNSimilarityProviderResultsAddResult(BNSimilarityProviderResults* results, + const BNSimilarityEntityRef* source, const BNSimilarityEntityRef* target, + uint8_t similarity, uint8_t confidence); + BINARYNINJACOREAPI BNSimilarityProviderType* BNSimilarityProviderGetType(BNSimilarityProvider* provider); + BINARYNINJACOREAPI BNSimilarityProviderId BNSimilarityProviderGetId(BNSimilarityProvider* provider); + BINARYNINJACOREAPI char* BNSimilarityProviderGetName(BNSimilarityProvider* provider, + BNSimilaritySessionNode* node, BNSimilarityEntityId entity, BNSimilarityResultId result); + BINARYNINJACOREAPI BNSimilarityApplyStatus BNSimilarityProviderApply(BNSimilarityProvider* provider, + BNSimilaritySessionNode* node, BNSimilarityEntityId entity, BNSimilarityResultId result); + BINARYNINJACOREAPI BNSimilarityApplyStatus BNSimilaritySessionNodeApplyTarget(BNSimilaritySessionNode* node, + BNSimilarityEntityId entity, const BNSimilarityEntityRef* target); + BINARYNINJACOREAPI void BNSimilarityProviderRender( + BNSimilarityProvider* provider, BNSimilaritySessionNode* node, BNSimilarityEntityId entity, + BNSimilarityRenderContext* context, BNSimilarityResultId result); + + BINARYNINJACOREAPI BNSimilarityRenderContext* BNCreateSimilarityRenderContext(void); + BINARYNINJACOREAPI void BNSimilarityRenderContextSetPreferredViewType( + BNSimilarityRenderContext* context, BNFunctionViewType type); + BINARYNINJACOREAPI BNFunctionGraphType BNSimilarityRenderContextGetPreferredViewType( + BNSimilarityRenderContext* context); + BINARYNINJACOREAPI char* BNSimilarityRenderContextGetPreferredViewTypeName(BNSimilarityRenderContext* context); + BINARYNINJACOREAPI void BNSimilarityRenderContextAddFlowGraph( + BNSimilarityRenderContext* context, const char* group, BNFlowGraph* graph); + BINARYNINJACOREAPI void BNSimilarityRenderContextAddFlowGraphForEntity( + BNSimilarityRenderContext* context, const char* group, BNFlowGraph* graph, const BNSimilarityEntityRef* entity); + BINARYNINJACOREAPI void BNSimilarityRenderContextAddLinearView( + BNSimilarityRenderContext* context, const char* group, BNBinaryView* data, BNLinearViewObject* linearView); + BINARYNINJACOREAPI void BNSimilarityRenderContextAddLinearViewForEntity(BNSimilarityRenderContext* context, + const char* group, BNBinaryView* data, BNLinearViewObject* linearView, const BNSimilarityEntityRef* entity); + BINARYNINJACOREAPI BNSimilarityView** BNGetSimilarityRenderContextViews( + BNSimilarityRenderContext* context, size_t* count); + BINARYNINJACOREAPI void BNFreeSimilarityViewList(BNSimilarityView** views, size_t count); + BINARYNINJACOREAPI char* BNSimilarityViewGetGroup(BNSimilarityView* view); + BINARYNINJACOREAPI BNSimilarityViewType BNSimilarityViewGetType(BNSimilarityView* view); + BINARYNINJACOREAPI BNFlowGraph* BNSimilarityViewGetFlowGraph(BNSimilarityView* view); + BINARYNINJACOREAPI BNBinaryView* BNSimilarityViewGetLinearViewData(BNSimilarityView* view); + BINARYNINJACOREAPI BNLinearViewObject* BNSimilarityViewGetLinearView(BNSimilarityView* view); + BINARYNINJACOREAPI bool BNSimilarityViewGetEntity(BNSimilarityView* view, BNSimilarityEntityRef* entity); + BINARYNINJACOREAPI BNSimilarityView* BNNewSimilarityViewReference(BNSimilarityView* view); + BINARYNINJACOREAPI void BNFreeSimilarityView(BNSimilarityView* view); + BINARYNINJACOREAPI BNSimilarityRenderContext* BNNewSimilarityRenderContextReference( + BNSimilarityRenderContext* context); + BINARYNINJACOREAPI void BNFreeSimilarityRenderContext(BNSimilarityRenderContext* context); + BINARYNINJACOREAPI BNDiffRenderer* BNCreateDiffRenderer(void); + BINARYNINJACOREAPI void BNDiffRendererAddRangeAnnotation( + BNDiffRenderer* renderer, uint64_t start, uint64_t end, BNSimilarityAnnotationType type); + BINARYNINJACOREAPI void BNDiffRendererRenderFunction( + BNDiffRenderer* renderer, BNSimilarityRenderContext* context, BNFunction* function); + BINARYNINJACOREAPI void BNDiffRendererRenderFunctionForEntity(BNDiffRenderer* renderer, + BNSimilarityRenderContext* context, BNFunction* function, const BNSimilarityEntityRef* entity); + BINARYNINJACOREAPI void BNDiffRendererRenderFlowGraph( + BNDiffRenderer* renderer, BNSimilarityRenderContext* context, const char* group, BNFlowGraph* graph); + BINARYNINJACOREAPI void BNDiffRendererRenderFlowGraphForEntity(BNDiffRenderer* renderer, + BNSimilarityRenderContext* context, const char* group, BNFlowGraph* graph, const BNSimilarityEntityRef* entity); + BINARYNINJACOREAPI void BNDiffRendererRenderLinearView(BNDiffRenderer* renderer, BNSimilarityRenderContext* context, + const char* group, BNBinaryView* data, BNLinearViewObject* linearView); + BINARYNINJACOREAPI void BNDiffRendererRenderLinearViewForEntity(BNDiffRenderer* renderer, + BNSimilarityRenderContext* context, const char* group, BNBinaryView* data, BNLinearViewObject* linearView, + const BNSimilarityEntityRef* entity); + BINARYNINJACOREAPI BNDiffRenderer* BNNewDiffRendererReference(BNDiffRenderer* renderer); + BINARYNINJACOREAPI void BNFreeDiffRenderer(BNDiffRenderer* renderer); + BINARYNINJACOREAPI BNSimilarityProvider* BNNewSimilarityProviderReference(BNSimilarityProvider* provider); + BINARYNINJACOREAPI void BNFreeSimilarityProvider(BNSimilarityProvider* provider); + BINARYNINJACOREAPI void BNFreeSimilarityResultIdList(BNSimilarityResultId* results); + + BINARYNINJACOREAPI BNSimilaritySessionResolverType* BNRegisterSimilaritySessionResolverType( + const char* name, const char* description, BNCustomSimilaritySessionResolverType* type); + BINARYNINJACOREAPI BNSimilaritySessionResolverType* BNGetSimilaritySessionResolverTypeByName(const char* name); + BINARYNINJACOREAPI BNSimilaritySessionResolverType** BNGetSimilaritySessionResolverTypeList(size_t* count); + BINARYNINJACOREAPI void BNFreeSimilaritySessionResolverTypeList(BNSimilaritySessionResolverType** types); + BINARYNINJACOREAPI char* BNSimilaritySessionResolverTypeGetName(BNSimilaritySessionResolverType* type); + BINARYNINJACOREAPI char* BNSimilaritySessionResolverTypeGetDescription(BNSimilaritySessionResolverType* type); + BINARYNINJACOREAPI BNSimilaritySessionResolver* BNSimilaritySessionResolverTypeCreateResolver( + BNSimilaritySessionResolverType* type, BNSimilaritySession* session, BNSettings* settings); + BINARYNINJACOREAPI BNSettings* BNSimilaritySessionResolverTypeGetDefaultSettings( + BNSimilaritySessionResolverType* type); + + BINARYNINJACOREAPI BNSimilaritySessionResolver* BNCreateCustomSimilaritySessionResolver( + BNSimilaritySessionResolverType* type, BNSimilaritySession* session, + BNCustomSimilaritySessionResolver* callbacks); + BINARYNINJACOREAPI BNSimilaritySessionResolverType* BNSimilaritySessionResolverGetType( + BNSimilaritySessionResolver* resolver); + BINARYNINJACOREAPI BNSimilaritySessionResolverId BNSimilaritySessionResolverGetId( + BNSimilaritySessionResolver* resolver); + BINARYNINJACOREAPI void BNSimilaritySessionResolverPrepareForNode( + BNSimilaritySessionResolver* resolver, BNSimilaritySession* session, BNSimilaritySessionNode* node, + BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI void BNSimilaritySessionResolverResolveForNode(BNSimilaritySessionResolver* resolver, + BNSimilaritySession* session, BNSimilaritySessionNode* node, const BNSimilarityEntityId* entities, + size_t entityCount, BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI BNSimilaritySessionResolver* BNNewSimilaritySessionResolverReference(BNSimilaritySessionResolver* resolver); + BINARYNINJACOREAPI void BNFreeSimilaritySessionResolver(BNSimilaritySessionResolver* resolver); + BINARYNINJACOREAPI void BNFreeSimilaritySessionResolverList(BNSimilaritySessionResolver** resolvers, size_t count); + + BINARYNINJACOREAPI BNSimilaritySessionNode* BNCreateSimilaritySessionNode(BNBinaryView* view); + BINARYNINJACOREAPI BNSimilaritySessionNode* BNCreateSimilaritySessionNodeFromFile(BNFileMetadata* file); + BINARYNINJACOREAPI BNBinaryView* BNSimilaritySessionNodeGetView(BNSimilaritySessionNode* node); + BINARYNINJACOREAPI void BNSimilaritySessionNodeSetView(BNSimilaritySessionNode* node, BNBinaryView* view); + BINARYNINJACOREAPI BNFileMetadata* BNSimilaritySessionNodeGetFile(BNSimilaritySessionNode* node); + BINARYNINJACOREAPI BNSettings* BNSimilaritySessionNodeGetLoadOptions(BNSimilaritySessionNode* node); + BINARYNINJACOREAPI BNSimilaritySessionNodeId BNSimilaritySessionNodeGetId(BNSimilaritySessionNode* node); + BINARYNINJACOREAPI BNSimilarityEntityId BNSimilaritySessionNodeCreateEntity( + BNSimilaritySessionNode* node, const BNSimilarityEntityInfo* info); + BINARYNINJACOREAPI bool BNSimilaritySessionNodeRemoveEntity( + BNSimilaritySessionNode* node, BNSimilarityEntityId id); + BINARYNINJACOREAPI bool BNSimilaritySessionNodeGetEntity( + BNSimilaritySessionNode* node, BNSimilarityEntityId id, BNSimilarityEntityInfo* result); + BINARYNINJACOREAPI void BNFreeSimilarityEntityInfo(BNSimilarityEntityInfo* info); + BINARYNINJACOREAPI BNSimilarityEntityId* BNSimilaritySessionNodeGetEntities( + BNSimilaritySessionNode* node, size_t* count); + BINARYNINJACOREAPI bool BNSimilaritySessionNodeAddScheduledEntity( + BNSimilaritySessionNode* node, BNSimilarityEntityId id); + BINARYNINJACOREAPI bool BNSimilaritySessionNodeRemoveScheduledEntity( + BNSimilaritySessionNode* node, BNSimilarityEntityId id); + BINARYNINJACOREAPI BNSimilarityEntityId* BNSimilaritySessionNodeGetScheduledEntities( + BNSimilaritySessionNode* node, size_t* count); + BINARYNINJACOREAPI BNFunction* BNSimilaritySessionNodeGetEntityFunction( + BNSimilaritySessionNode* node, BNSimilarityEntityId id); + BINARYNINJACOREAPI BNSimilarityResultId* BNSimilaritySessionNodeGetResults( + BNSimilaritySessionNode* node, BNSimilarityEntityId entity, size_t* count); + BINARYNINJACOREAPI bool BNSimilaritySessionNodeGetResult( + BNSimilaritySessionNode* node, BNSimilarityResultId resultId, BNSimilarityResult* result); + BINARYNINJACOREAPI bool BNSimilaritySessionNodeSetResolvedResult( + BNSimilaritySessionNode* node, BNSimilarityEntityId entity, BNSimilarityResultId result); + BINARYNINJACOREAPI bool BNSimilaritySessionNodeGetResolvedResult( + BNSimilaritySessionNode* node, BNSimilarityEntityId entity, BNSimilarityResultId* result); + BINARYNINJACOREAPI bool BNSimilaritySessionNodeClearResolvedResult( + BNSimilaritySessionNode* node, BNSimilarityEntityId entity); + BINARYNINJACOREAPI void BNFreeSimilarityEntityList(BNSimilarityEntityId* entities); + BINARYNINJACOREAPI BNSimilaritySessionNodeId* BNSimilaritySessionNodeGetIncomingEdges(BNSimilaritySessionNode* node, size_t* count); + BINARYNINJACOREAPI BNSimilaritySessionNodeId* BNSimilaritySessionNodeGetOutgoingEdges(BNSimilaritySessionNode* node, size_t* count); + BINARYNINJACOREAPI void BNFreeSimilaritySessionNodeEdgeList(BNSimilaritySessionNodeId* edges); + BINARYNINJACOREAPI BNSimilaritySessionNode** BNSimilaritySessionNodeGetIncomingNodes( + BNSimilaritySessionNode* node, size_t* count); + BINARYNINJACOREAPI BNSimilaritySessionNode** BNSimilaritySessionNodeGetOutgoingNodes( + BNSimilaritySessionNode* node, size_t* count); + BINARYNINJACOREAPI BNSimilaritySessionNode* BNNewSimilaritySessionNodeReference(BNSimilaritySessionNode* node); + BINARYNINJACOREAPI void BNFreeSimilaritySessionNode(BNSimilaritySessionNode* node); + + BINARYNINJACOREAPI void BNSimilaritySessionGraphAddNode( + BNSimilaritySessionGraph* graph, BNSimilaritySessionNode* node); + BINARYNINJACOREAPI void BNSimilaritySessionGraphRemoveNode( + BNSimilaritySessionGraph* graph, BNSimilaritySessionNode* node); + BINARYNINJACOREAPI bool BNSimilaritySessionGraphIsValidEdge( + BNSimilaritySessionGraph* graph, BNSimilaritySessionNode* from, BNSimilaritySessionNode* to); + BINARYNINJACOREAPI bool BNSimilaritySessionGraphAddEdge( + BNSimilaritySessionGraph* graph, BNSimilaritySessionNode* from, BNSimilaritySessionNode* to); + BINARYNINJACOREAPI bool BNSimilaritySessionGraphRemoveEdge( + BNSimilaritySessionGraph* graph, BNSimilaritySessionNode* from, BNSimilaritySessionNode* to); + BINARYNINJACOREAPI BNSimilaritySessionNode* BNSimilaritySessionGraphGetNode( + BNSimilaritySessionGraph* graph, BNSimilaritySessionNodeId id); + BINARYNINJACOREAPI BNSimilaritySessionNode** BNSimilaritySessionGraphGetNodes( + BNSimilaritySessionGraph* graph, size_t* count); + BINARYNINJACOREAPI void BNFreeSimilaritySessionNodeList(BNSimilaritySessionNode** nodes, size_t count); + BINARYNINJACOREAPI BNSimilaritySessionNode*** BNSimilaritySessionGraphGetSchedule( + BNSimilaritySessionGraph* graph, size_t** nodeCounts, size_t* levelCount); + BINARYNINJACOREAPI void BNFreeSimilaritySessionNodeSchedule( + BNSimilaritySessionNode*** schedule, size_t* nodeCounts, size_t levelCount); + BINARYNINJACOREAPI BNSimilaritySessionGraph* BNNewSimilaritySessionGraphReference(BNSimilaritySessionGraph* node); + BINARYNINJACOREAPI void BNFreeSimilaritySessionGraph(BNSimilaritySessionGraph* graph); + BINARYNINJACOREAPI BNSimilaritySessionGraphReceiver* BNCreateCustomSimilaritySessionGraphReceiver( + BNCustomSimilaritySessionGraphReceiver* callbacks); + BINARYNINJACOREAPI BNSimilaritySessionGraphReceiver* BNNewSimilaritySessionGraphReceiverReference( + BNSimilaritySessionGraphReceiver* receiver); + BINARYNINJACOREAPI void BNFreeSimilaritySessionGraphReceiver(BNSimilaritySessionGraphReceiver* receiver); + BINARYNINJACOREAPI void BNFreeSimilaritySessionGraphReceiverList( + BNSimilaritySessionGraphReceiver** receivers, size_t count); + BINARYNINJACOREAPI void BNSimilaritySessionGraphReceiverNotifyGraphChanged( + BNSimilaritySessionGraphReceiver* receiver); + BINARYNINJACOREAPI void BNSimilaritySessionGraphAddReceiver( + BNSimilaritySessionGraph* graph, BNSimilaritySessionGraphReceiver* receiver); + BINARYNINJACOREAPI void BNSimilaritySessionGraphRemoveReceiver( + BNSimilaritySessionGraph* graph, BNSimilaritySessionGraphReceiver* receiver); + BINARYNINJACOREAPI BNSimilaritySessionGraphReceiver** BNSimilaritySessionGraphGetReceivers( + BNSimilaritySessionGraph* graph, size_t* count); + + BINARYNINJACOREAPI BNSimilaritySession* BNCreateSimilaritySession(); + BINARYNINJACOREAPI BNSimilaritySessionId BNSimilaritySessionGetId(BNSimilaritySession* session); + BINARYNINJACOREAPI BNSimilaritySessionReceiver* BNCreateCustomSimilaritySessionReceiver( + BNCustomSimilaritySessionReceiver* callbacks); + BINARYNINJACOREAPI BNSimilaritySessionReceiver* BNNewSimilaritySessionReceiverReference( + BNSimilaritySessionReceiver* receiver); + BINARYNINJACOREAPI void BNFreeSimilaritySessionReceiver(BNSimilaritySessionReceiver* receiver); + BINARYNINJACOREAPI void BNFreeSimilaritySessionReceiverList(BNSimilaritySessionReceiver** receivers, size_t count); + BINARYNINJACOREAPI void BNSimilaritySessionReceiverNotifyStart( + BNSimilaritySessionReceiver* receiver, BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI void BNSimilaritySessionReceiverNotifyBatch(BNSimilaritySessionReceiver* receiver, + BNSimilaritySessionNode* node, BNSimilarityProvider* provider, + const BNSimilarityEntityId* entities, size_t count); + BINARYNINJACOREAPI void BNSimilaritySessionAddProvider( + BNSimilaritySession* session, BNSimilarityProvider* provider); + BINARYNINJACOREAPI void BNSimilaritySessionRemoveProvider( + BNSimilaritySession* session, BNSimilarityProvider* provider); + BINARYNINJACOREAPI bool BNSimilaritySessionUpdateProviderSettings( + BNSimilaritySession* session, BNSimilarityProvider* provider, BNSettings* settings); + BINARYNINJACOREAPI BNSimilarityProvider* BNSimilaritySessionGetProvider( + BNSimilaritySession* session, BNSimilarityProviderId id); + BINARYNINJACOREAPI BNSimilarityProvider** BNSimilaritySessionGetProviders( + BNSimilaritySession* session, size_t* count); + BINARYNINJACOREAPI void BNFreeSimilarityProviderList(BNSimilarityProvider** providers, size_t count); + BINARYNINJACOREAPI bool BNSimilaritySessionAddResolver( + BNSimilaritySession* session, BNSimilaritySessionResolver* resolver); + BINARYNINJACOREAPI bool BNSimilaritySessionRemoveResolver( + BNSimilaritySession* session, BNSimilaritySessionResolver* resolver); + BINARYNINJACOREAPI bool BNSimilaritySessionUpdateResolverSettings( + BNSimilaritySession* session, BNSimilaritySessionResolver* resolver, BNSettings* settings); + BINARYNINJACOREAPI BNSimilaritySessionResolver* BNSimilaritySessionGetResolver( + BNSimilaritySession* session, BNSimilaritySessionResolverId id); + BINARYNINJACOREAPI BNSimilaritySessionResolver** BNSimilaritySessionGetResolvers( + BNSimilaritySession* session, size_t* count); + BINARYNINJACOREAPI void BNSimilaritySessionAddReceiver( + BNSimilaritySession* session, BNSimilaritySessionReceiver* receiver); + BINARYNINJACOREAPI void BNSimilaritySessionRemoveReceiver( + BNSimilaritySession* session, BNSimilaritySessionReceiver* receiver); + BINARYNINJACOREAPI BNSimilaritySessionReceiver** BNSimilaritySessionGetReceivers( + BNSimilaritySession* session, size_t* count); + BINARYNINJACOREAPI BNSimilaritySessionGraph* BNSimilaritySessionGetGraph(BNSimilaritySession* session); + BINARYNINJACOREAPI BNSimilaritySessionCompletion* BNSimilaritySessionRun(BNSimilaritySession* session); + BINARYNINJACOREAPI BNSimilaritySession* BNNewSimilaritySessionReference(BNSimilaritySession* session); + BINARYNINJACOREAPI void BNFreeSimilaritySession(BNSimilaritySession* session); + + BINARYNINJACOREAPI BNSimilaritySessionCompletion* BNCreateSimilaritySessionCompletion(); + BINARYNINJACOREAPI bool BNSimilaritySessionCompletionIsFinished(BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI void BNSimilaritySessionCompletionRequestStop(BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI bool BNSimilaritySessionCompletionIsStopRequested(BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI double BNSimilaritySessionCompletionGetProgress( + BNSimilaritySessionCompletion* completion, const BNSimilaritySessionCompletionQuery* query); + BINARYNINJACOREAPI void BNSimilaritySessionCompletionSetProgress( + BNSimilaritySessionCompletion* completion, const BNSimilaritySessionCompletionQuery* query, double progress); + BINARYNINJACOREAPI uint64_t BNSimilaritySessionCompletionGetTiming( + BNSimilaritySessionCompletion* completion, const BNSimilaritySessionCompletionQuery* query); + BINARYNINJACOREAPI BNSimilaritySessionCompletion* BNNewSimilaritySessionCompletionReference(BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI void BNFreeSimilaritySessionCompletion(BNSimilaritySessionCompletion* completion); + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI char* BNAllocStringWithLength(const char* contents, size_t len); BINARYNINJACOREAPI void BNFreeString(char* str); diff --git a/docs/dev/batch.md b/docs/dev/batch.md index d97a69ccab..589d9d9cf8 100644 --- a/docs/dev/batch.md +++ b/docs/dev/batch.md @@ -55,7 +55,7 @@ While you might associate actions with UI based actions in the product, many act ```python cxt = PluginCommandContext(bv) -PluginCommand.get_valid_list(cxt)['BinExport (Quick)'].execute(cxt) +PluginCommand.get_valid_list(cxt)['BinExport'].execute(cxt) ``` ### Multiple files @@ -189,4 +189,3 @@ import binaryninja ``` Other alternative solutions include setting the environment variable before running your script, or manually moving your settings file from your [user folder](../guide/index.md#user-folder) before running your automation. - diff --git a/docs/guide/binexport.md b/docs/guide/binexport.md index f5de576544..0eb3b9e21a 100644 --- a/docs/guide/binexport.md +++ b/docs/guide/binexport.md @@ -1,55 +1,43 @@ # BinExport / BinDiff -BinExport is a [plugin written by Google](https://github.com/google/binexport) (originally [Zynamics](https://www.zynamics.com/) before their acquisition) to create files used for the [BinDiff](https://github.com/google/bindiff) differential analysis tool. +Binary Ninja includes a native similarity provider based on Google's +[BinDiff](https://github.com/google/bindiff) project. The same plugin can export +[BinExport](https://github.com/google/binexport) files for use with the standalone BinDiff tools. -## Usage +## Exporting from the UI -### Activation +Run **Plugins > BinExport**, or select **BinExport** in the +[command palette](./index.md#command-palette). Choose the destination for the `.BinExport` file in the save dialog. +Export failures are reported in the log and in the UI. -![BinExport Success >](../img/binexport-success.png "BinExport Success"){ width="300" } - -To trigger the plugin and create a `.binexport` file for the current binary, use either the `Plugins` / `BinExport` menu, or the `BinExport` action in the [command-palette](./index.md#command-palette). - -A file dialog will open, defaulting to the same file name and location (without the original file extension -- a new file extension will automatically be appended of .BinExport`) - -If successful, two more dialogs will show up. One warning that the export can take some time for large files, and a second confirming success. - -### What Next? - -After creating the before and after `.BinExport` files you wish to analyze, you'll want to install [BinDiff](https://github.com/google/bindiff), and run it on the CLI or the GUI to create a `.bindiff` result database. +After exporting the binaries to compare, run BinDiff on the two files using its command-line or graphical interface: ```sh -$ bindiff writable.BinExport writable2.BinExport -BinDiff 7 (@377901646, Jun 7 2021), (c)2004-2011 zynamics GmbH, (c)2011-2021 Google LLC. -Setup: 0s -primary: writable: 11 functions, 2 calls -secondary: writable2: 11 functions, 2 calls -Matching: 0s -matched: 11 of 11/11 (primary/secondary, 11/11 non-library) -call graph MD index: primary 0.264073 - secondary 0.264073 -Similarity: 99.2002% (Confidence: 99.2002%) -Writing results: 0.0s +bindiff baseline.BinExport modified.BinExport ``` -The resulting `.BinDiff` file can either be opened in the BinDiff Java GUI, or directly in Binary Ninja directly using the [BD Viewer](https://github.com/PistonMiner/binaryninja-bindiff-viewer) plugin (available via the [Plugin Manager](./plugins.md)) and also supports porting symbols from one matched database to another. +## Exporting headlessly -![BD Viewer](../img/binexport-bdviewer.png "BD Viewer"){ width="800" } -_BD Viewer Plugin_ - -### Headless - -If you want to trigger the plugin headlessly, you can use the exposed quick action which will automatically write a `.BinExport` in the same folder as the source file: +The same command works headlessly. It writes a `.BinExport` file next to the source binary using the source filename: ```python -cxt = PluginCommandContext(bv) -PluginCommand.get_valid_list(cxt)['BinExport (Quick)'].execute(cxt) +from binaryninja import PluginCommand, PluginCommandContext + +context = PluginCommandContext(bv) +PluginCommand.get_valid_list(context)["BinExport"].execute(context) ``` -### Disabling +The source view must have a filename when the command is run headlessly. + +## Built-in similarity + +The **Google BinDiff** provider can also compare binaries directly in a +[similarity session](./similarity.md), without first creating intermediate BinExport files. -If desired, the plugin can be disabled from loading using the `corePlugins.binexport` setting. +## Disabling -## Changes / Fork +Disable `corePlugins.bindiffSimilarity` to prevent the bundled plugin from loading. This disables both the Google +BinDiff similarity provider and the BinExport command. -All changes made to the original plugin to be able to ship it as a plugin directly with the product are maintained in the [Vector 35 fork](https://github.com/Vector35/binexport) and are [released](https://github.com/Vector35/binexport/blob/main/LICENSE) under the same Apache 2.0 license as the [original plugin](https://github.com/google/binexport/blob/main/LICENSE). Please do not file issues related to anything on that repo except for specific Binary Ninja UI functionality or changes. +BinExport and BinDiff are Google projects distributed under the Apache 2.0 license. See +[Open Source Components](../about/open-source.md) for attribution. diff --git a/docs/guide/plugins.md b/docs/guide/plugins.md index 69c7ebaf6e..ae4251fc91 100644 --- a/docs/guide/plugins.md +++ b/docs/guide/plugins.md @@ -1,6 +1,6 @@ # Using Plugins -The most common Binary Ninja plugins are written in Python which we are covering here. That said, there are some C++ plugins which must be built for the appropriate native architecture and will usually include build instructions for each platform. Several [C++ examples](https://github.com/Vector35/binaryninja-api/tree/dev/examples) are included in the API repository, and the [binexport](https://github.com/google/binexport) utility (used with [bindiff](https://www.zynamics.com/bindiff.html)) is also a native plugin that must be built and installed manually. Finally, there is preliminary support for [Rust plugins](https://github.com/Vector35/binaryninja-api/tree/dev/rust), but the Rust API is still in the early stages of development, and should be considered a moving target, so proceed with caution and develop at your own risk. +The most common Binary Ninja plugins are written in Python which we are covering here. That said, there are some C++ plugins which must be built for the appropriate native architecture and will usually include build instructions for each platform. Several [C++ examples](https://github.com/Vector35/binaryninja-api/tree/dev/examples) are included in the API repository. Binary Ninja also bundles native plugins such as the [Google BinDiff similarity and BinExport integration](./binexport.md). Finally, there is preliminary support for [Rust plugins](https://github.com/Vector35/binaryninja-api/tree/dev/rust), but the Rust API is still in the early stages of development, and should be considered a moving target, so proceed with caution and develop at your own risk. Plugins are loaded from the user's plugin folder: diff --git a/plugins/bindiff_similarity/CMakeLists.txt b/plugins/bindiff_similarity/CMakeLists.txt new file mode 100644 index 0000000000..aee79093d3 --- /dev/null +++ b/plugins/bindiff_similarity/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) + +project(bindiff_similarity) + +file(GLOB SOURCES CONFIGURE_DEPENDS + *.cpp + *.c + *.h) + +if (DEMO) + add_library(${PROJECT_NAME} STATIC ${SOURCES}) +else () + add_library(${PROJECT_NAME} SHARED ${SOURCES}) +endif () + +if (NOT BN_INTERNAL_BUILD) + # Out-of-tree build + find_path( + BN_API_PATH + NAMES binaryninjaapi.h + HINTS ../../.. binaryninjaapi $ENV{BN_API_PATH} + REQUIRED + ) + add_subdirectory(${BN_API_PATH} api) +endif () + +set(BINDIFF_BUILD_TESTING OFF CACHE BOOL "" FORCE) +set(BINDIFF_ENABLE_IDAPRO OFF CACHE BOOL "" FORCE) +set(BINDIFF_ENABLE_IPO OFF CACHE BOOL "" FORCE) +set(BINEXPORT_BUILD_TESTING OFF CACHE BOOL "" FORCE) +set(BINEXPORT_ENABLE_IDAPRO OFF CACHE BOOL "" FORCE) +set(BINEXPORT_ENABLE_BINARYNINJA OFF CACHE BOOL "" FORCE) +set(BINEXPORT_MSVC_STATIC_RUNTIME OFF CACHE BOOL "" FORCE) + +add_subdirectory( + "${CMAKE_CURRENT_LIST_DIR}/../../vendor/bindiff" + "${CMAKE_CURRENT_BINARY_DIR}/vendor_bindiff" + EXCLUDE_FROM_ALL +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + binaryninjaapi + binexport_core + bindiff_base + bindiff_shared +) + +if (APPLE) + target_link_options(${PROJECT_NAME} PRIVATE + "LINKER:-exported_symbol,_CorePluginABIVersion" + "LINKER:-exported_symbol,_CorePluginInit") +elseif (UNIX) + target_link_options(${PROJECT_NAME} PRIVATE "LINKER:--exclude-libs,ALL") +endif () + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 20 + 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(${PROJECT_NAME}) + set_target_properties(${PROJECT_NAME} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) + +else () + bn_install_plugin(${PROJECT_NAME}) +endif () diff --git a/plugins/bindiff_similarity/README.md b/plugins/bindiff_similarity/README.md new file mode 100644 index 0000000000..0f069d2d89 --- /dev/null +++ b/plugins/bindiff_similarity/README.md @@ -0,0 +1,6 @@ +# BinDiff Similarity Provider + +This plugin uses Google BinDiff to compare the functions scheduled on connected similarity-session nodes. It exposes +bidirectional function matches, ports the matched function's symbol and type, and renders graph and linear diff views. + +Each node is compared with its incoming neighbors, so the session graph controls which binaries are compared. diff --git a/plugins/bindiff_similarity/diffview.cpp b/plugins/bindiff_similarity/diffview.cpp new file mode 100644 index 0000000000..b60746bb32 --- /dev/null +++ b/plugins/bindiff_similarity/diffview.cpp @@ -0,0 +1,50 @@ +#include "diffview.h" + +#include "processor.h" + +#include + +std::unique_ptr BinDiffView::FromFilePath(const std::string& filePath) +{ + auto view = std::make_unique(); + security::bindiff::Instruction::Cache instrCache; + auto status = security::bindiff::Read( + filePath, &view->m_callGraph, &view->m_flowGraphs, &view->m_flowGraphInfos, &instrCache); + if (!status.ok()) + { + BinaryNinja::LogErrorF("Failed to read bindiff data from {}: {}", filePath, status.message()); + return nullptr; + } + return view; +} + +std::unique_ptr BinDiffView::FromSessionNode( + BinaryNinja::SimilaritySessionNode& node, const std::function& progress) +{ + if (!progress(0.0)) + return nullptr; + // NOTE: The view is always available during visitation. + BinDiffProcessor processor(*node.GetView()); + // BinDiff looks at the call graph of the binary so we actually need to construct the view from all known entities + // not just the ones we expect to visit (think process vs. matching). + const auto entities = node.GetEntities(); + for (size_t i = 0; i < entities.size(); ++i) + { + if (((i & 0xff) == 0) && !progress(0.6 * static_cast(i) / entities.size())) + return nullptr; + const auto entity = entities[i]; + const auto entityInfo = node.GetEntity(entity); + if (!entityInfo || entityInfo->type != SimilarityEntityFunction) + continue; + BinaryNinja::Ref function = node.GetEntityFunction(entity); + if (function) + processor.AddFunction(*function); + } + BinaryNinja::Ref tmpFile = new BinaryNinja::TemporaryFile(); + if (!processor.Process(tmpFile->GetPath(), [&](double value) { return progress(0.6 + 0.35 * value); })) + return nullptr; + auto viewResult = FromFilePath(tmpFile->GetPath()); + if (!progress(1.0)) + return nullptr; + return viewResult; +} diff --git a/plugins/bindiff_similarity/diffview.h b/plugins/bindiff_similarity/diffview.h new file mode 100644 index 0000000000..a5582d405a --- /dev/null +++ b/plugins/bindiff_similarity/diffview.h @@ -0,0 +1,29 @@ +#pragma once + +#include "binaryninjaapi.h" + +#include "third_party/zynamics/bindiff/call_graph.h" +#include "third_party/zynamics/bindiff/flow_graph.h" +#include "third_party/zynamics/bindiff/reader.h" + +#include +#include +#include + +class BinDiffView +{ +public: + security::bindiff::CallGraph m_callGraph; + security::bindiff::FlowGraphs m_flowGraphs; + security::bindiff::FlowGraphInfos m_flowGraphInfos; + + AddressSpace m_addressSpace; + + BinDiffView() = default; + + static std::unique_ptr FromFilePath(const std::string& filePath); + + // Generate a view from the given node, the nodes view must be available before calling. + static std::unique_ptr FromSessionNode( + BinaryNinja::SimilaritySessionNode& node, const std::function& progress); +}; diff --git a/plugins/bindiff_similarity/plugin.cpp b/plugins/bindiff_similarity/plugin.cpp new file mode 100644 index 0000000000..8d005a7d44 --- /dev/null +++ b/plugins/bindiff_similarity/plugin.cpp @@ -0,0 +1,66 @@ +#include + +#include "processor.h" +#include "provider.h" + +using namespace BinaryNinja; + +namespace { + void ExportBinExport(BinaryView* view) + { + const std::string inputFilename = view->GetFile()->GetFilename(); + std::string outputPath = inputFilename.empty() ? "export.BinExport" : inputFilename; + if (!inputFilename.empty()) + { + const size_t separator = outputPath.find_last_of("/\\"); + const size_t filenameStart = separator == std::string::npos ? 0 : separator + 1; + const size_t extension = outputPath.find_last_of('.'); + if ((extension != std::string::npos) && (extension > filenameStart)) + outputPath.resize(extension); + outputPath += ".BinExport"; + } + + if (IsUIEnabled()) + { + std::string selectedPath; + if (!GetSaveFileNameInput(selectedPath, "Export BinExport", "BinExport files (*.BinExport)", outputPath)) + return; + outputPath = selectedPath; + } + else if (inputFilename.empty()) + { + LogError("Cannot export an unsaved view to BinExport in headless mode"); + return; + } + + bool success; + { + BinDiffProcessor processor(*view); + for (const auto& function : view->GetAnalysisFunctionList()) + processor.AddFunction(*function); + success = processor.Process(outputPath); + } + if (!success && IsUIEnabled()) + { + ShowMessageBox("BinExport Failed", "The BinExport file could not be written. See the log for details.", + OKButtonSet, ErrorIcon); + } + } +} // namespace + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifdef DEMO_EDITION + bool GoogleBinDiffPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + PluginCommand::Register("BinExport", "Export to BinDiff binary", ExportBinExport); + + SimilarityProviderType::Register(new GoogleSimilarityProviderType()); + return true; + } +} diff --git a/plugins/bindiff_similarity/processor.cpp b/plugins/bindiff_similarity/processor.cpp new file mode 100644 index 0000000000..e5f5d18c0e --- /dev/null +++ b/plugins/bindiff_similarity/processor.cpp @@ -0,0 +1,426 @@ +#include "processor.h" + +#include "third_party/absl/container/flat_hash_set.h" +#include "third_party/absl/strings/ascii.h" +#include "third_party/zynamics/binexport/basic_block.h" +#include "third_party/zynamics/binexport/binexport2_writer.h" +#include "third_party/zynamics/binexport/call_graph.h" +#include "third_party/zynamics/binexport/flow_graph.h" +#include "third_party/zynamics/binexport/instruction.h" +#include "third_party/zynamics/binexport/util/filesystem.h" +#include "third_party/zynamics/binexport/address_references.h" + +#include + +using namespace security::binexport; + +namespace { + std::mutex g_instructionStateMutex; + + int GetSegmentPermissions(const BinaryNinja::Segment& segment) + { + int permissions = 0; + const uint32_t segment_flags = segment.GetFlags(); + if (segment_flags & SegmentExecutable) + permissions |= AddressSpace::kExecute; + if (segment_flags & SegmentWritable) + permissions |= AddressSpace::kWrite; + if (segment_flags & SegmentReadable) + permissions |= AddressSpace::kRead; + return permissions; + } + + std::optional FindMnemonic(const std::vector& instrTokens) + { + for (size_t i = 0; i < instrTokens.size(); i++) + { + if (instrTokens[i].type == InstructionToken) + return i; + } + return std::nullopt; + } + + Instruction ParseInstructionBinaryNinja(uint64_t address, const BinaryNinja::InstructionInfo& instruction, + const std::vector& instrTokens) + { + const auto mnemonicIndex = FindMnemonic(instrTokens); + if (!mnemonicIndex) + return Instruction(address); + std::string mnemonic = instrTokens[*mnemonicIndex].text; + absl::StripAsciiWhitespace(&mnemonic); + + const Address nextInstrAddr = address + instruction.length; + return Instruction(address, nextInstrAddr, instruction.length, mnemonic, {}); + } +} // namespace + +struct BinDiffProcessor::Impl +{ + struct BasicBlockRange + { + Address start; + Address end; + }; + + struct FunctionTopology + { + Address entryPoint; + std::vector basicBlocks; + std::vector flowEdges; + std::vector> callEdges; + }; + + std::unique_lock m_instructionStateLock {g_instructionStateMutex}; + BinaryNinja::Ref m_view; + AddressReferences m_addressReferences; + AddressSpace m_addressSpace; + AddressSpace m_flagSpace; + std::unique_ptr m_callGraph; + std::unique_ptr m_flowGraph; + Instructions m_instructions; + std::vector m_functionTopologies; + absl::flat_hash_set
m_functionStarts; +}; + + +BinDiffProcessor::BinDiffProcessor(BinaryNinja::BinaryView& view) +{ + m_impl = std::make_unique(); + m_impl->m_view = &view; + m_impl->m_addressReferences = {}; + m_impl->m_addressSpace = {}; + m_impl->m_flagSpace = {}; + m_impl->m_callGraph = std::make_unique(); + m_impl->m_flowGraph = std::make_unique(); + m_impl->m_instructions = {}; + for (const auto& function : view.GetAnalysisFunctionList()) + m_impl->m_functionStarts.insert(function->GetStart()); + + for (const auto& segment : view.GetSegments()) + { + const uint64_t segmentAddr = segment->GetStart(); + const uint64_t segmentLen = segment->GetLength(); + const int permissions = GetSegmentPermissions(*segment); + + // Map the segment without reading its contents. + m_impl->m_flagSpace.AddMemoryBlock(segmentAddr, AddressSpace::MemoryBlock(segmentLen), permissions); + } + + const auto architecture = view.GetDefaultArchitecture(); + const int bitness = architecture ? static_cast(architecture->GetAddressSize()) * 8 : 32; + Instruction::SetBitness(bitness); + Instruction::SetMemoryFlags(&m_impl->m_flagSpace); +} + +BinDiffProcessor::~BinDiffProcessor() +{ + Instruction::SetGetBytesCallback({}); + Instruction::SetMemoryFlags(nullptr); + Operand::EmptyCache(); + Expression::EmptyCache(); +} + +void BinDiffProcessor::AddFunction(BinaryNinja::Function& func) +{ + BinaryNinja::Ref view = func.GetView(); + const auto basicBlocks = func.GetBasicBlocks(); + auto& topology = m_impl->m_functionTopologies.emplace_back(); + topology.entryPoint = func.GetStart(); + topology.basicBlocks.reserve(basicBlocks.size()); + std::vector
basicBlockStarts; + basicBlockStarts.reserve(basicBlocks.size()); + for (const auto& block : basicBlocks) + { + basicBlockStarts.push_back(block->GetStart()); + topology.basicBlocks.push_back({block->GetStart(), block->GetEnd()}); + } + std::sort(basicBlockStarts.begin(), basicBlockStarts.end()); + + m_impl->m_callGraph->AddFunction(func.GetStart()); + + Instruction::SetGetBytesCallback([view](const Instruction& instr) -> std::string { + const BinaryNinja::DataBuffer buffer = view->ReadBuffer(instr.GetAddress(), instr.GetSize()); + const size_t length = buffer.GetLength(); + if (length == 0) + return {}; + return {static_cast(buffer.GetData()), length}; + }); + + auto ProcessInstruction = [this, view, &func, &basicBlockStarts, &topology]( + const BinaryNinja::InstructionInfo& instrInfo, Instruction& instr) { + const uint64_t instrAddr = instr.GetAddress(); + // TODO: Really dumb optimization to get some large binaries to finish in time. + // TODO: I am sure there is a better way to express this but I think we punt on that for now. + const auto isOwnedBlock = [&](const Address address) { + return std::ranges::binary_search(basicBlockStarts, address); + }; + bool isUnresolvedBranch = false; + bool isFallthrough = instrInfo.branchCount == 0; + for (int branchIdx = 0; branchIdx < instrInfo.branchCount; branchIdx++) + { + const uint64_t branchTarget = instrInfo.branchTarget[branchIdx]; + switch (instrInfo.branchType[branchIdx]) + { + case IndirectBranch: + if ((branchTarget != 0) && isOwnedBlock(branchTarget)) + topology.flowEdges.emplace_back(instrAddr, branchTarget, FlowGraphEdge::TYPE_UNCONDITIONAL); + break; + case UnconditionalBranch: + case UserDefinedBranch: + case ExceptionBranch: + if (branchTarget != 0) + { + if (!isOwnedBlock(branchTarget)) + { + if (m_impl->m_functionStarts.contains(branchTarget)) + { + m_impl->m_callGraph->AddFunction(branchTarget); + topology.callEdges.emplace_back(instrAddr, branchTarget); + instr.SetFlag(FLAG_CALL, true); + m_impl->m_addressReferences.emplace_back( + instrAddr, GetSourceExpressionId(instr, branchTarget), branchTarget, TYPE_CALL_DIRECT); + } + break; + } + topology.flowEdges.emplace_back(instrAddr, branchTarget, FlowGraphEdge::TYPE_UNCONDITIONAL); + } + break; + case CallDestination: + if (m_impl->m_functionStarts.contains(branchTarget)) + { + m_impl->m_callGraph->AddFunction(branchTarget); + topology.callEdges.emplace_back(instrAddr, branchTarget); + } + ABSL_FALLTHROUGH_INTENDED; + case SystemCall: + instr.SetFlag(FLAG_CALL, true); + m_impl->m_addressReferences.emplace_back( + instrAddr, GetSourceExpressionId(instr, branchTarget), branchTarget, TYPE_CALL_DIRECT); + isFallthrough = true; + break; + case TrueBranch: + if (!isOwnedBlock(branchTarget)) + break; + topology.flowEdges.emplace_back(instrAddr, branchTarget, FlowGraphEdge::TYPE_TRUE); + break; + case FalseBranch: + if (!isOwnedBlock(branchTarget)) + break; + topology.flowEdges.emplace_back(instrAddr, branchTarget, FlowGraphEdge::TYPE_FALSE); + break; + case FunctionReturn: + break; + case UnresolvedBranch: + isUnresolvedBranch = true; + break; + default: + break; + } + } + + instr.SetFlag(FLAG_FLOW, isFallthrough); + + if (isUnresolvedBranch) + { + BinaryNinja::ReferenceSource refSource = {&func, func.GetArchitecture(), instrAddr}; + for (const auto& xref : view->GetCodeReferencesFrom(refSource)) + { + if (!isOwnedBlock(xref)) + continue; + topology.flowEdges.emplace_back(instrAddr, xref, FlowGraphEdge::TYPE_SWITCH); + } + } + }; + + BinaryNinja::InstructionInfo instrInfo = {}; + uint8_t instrData[BN_MAX_INSTRUCTION_LENGTH] = {}; + std::vector instrTokens = {}; + instrTokens.reserve(10); + + for (const auto& block : basicBlocks) + { + const auto blockArch = block->GetArchitecture(); + const size_t maxInstrLen = blockArch->GetMaxInstructionLength(); + uint64_t instrAddr = block->GetStart(); + + while (instrAddr < block->GetEnd()) + { + const size_t bufferLen = view->Read(instrData, instrAddr, maxInstrLen); + if ((bufferLen == 0) || !blockArch->GetInstructionInfo(instrData, instrAddr, bufferLen, instrInfo)) + break; + instrTokens.clear(); + if (!blockArch->GetInstructionText(instrData, instrAddr, instrInfo.length, instrTokens)) + break; + + Instruction instrBd = ParseInstructionBinaryNinja(instrAddr, instrInfo, instrTokens); + ProcessInstruction(instrInfo, instrBd); + m_impl->m_instructions.push_back(instrBd); + instrAddr += instrInfo.length; + } + } +} + +bool BinDiffProcessor::Process(const std::string& exportFilePath, std::function progress) +{ + if (!progress(0.0)) + return false; + SortInstructions(&m_impl->m_instructions); + m_impl->m_instructions.erase(std::unique(m_impl->m_instructions.begin(), m_impl->m_instructions.end(), + [](const Instruction& left, const Instruction& right) { return left.GetAddress() == right.GetAddress(); }), + m_impl->m_instructions.end()); + if (!progress(0.05)) + return false; + + std::sort(m_impl->m_addressReferences.begin(), m_impl->m_addressReferences.end()); + m_impl->m_addressReferences.erase( + std::unique(m_impl->m_addressReferences.begin(), m_impl->m_addressReferences.end()), + m_impl->m_addressReferences.end()); + if (!progress(0.1)) + return false; + + std::vector
blockBoundaries; + for (const auto& topology : m_impl->m_functionTopologies) + { + for (const auto& block : topology.basicBlocks) + { + blockBoundaries.push_back(block.start); + blockBoundaries.push_back(block.end); + } + } + std::sort(blockBoundaries.begin(), blockBoundaries.end()); + blockBoundaries.erase(std::unique(blockBoundaries.begin(), blockBoundaries.end()), blockBoundaries.end()); + + auto& functions = m_impl->m_flowGraph->GetFunctions(); + for (size_t topologyIndex = 0; topologyIndex < m_impl->m_functionTopologies.size(); ++topologyIndex) + { + if (((topologyIndex & 0x3ff) == 0) && !progress(0.1 + + (0.55 * static_cast(topologyIndex) / m_impl->m_functionTopologies.size()))) + return false; + + const auto& topology = m_impl->m_functionTopologies[topologyIndex]; + auto function = std::make_unique(topology.entryPoint); + std::vector edges = topology.flowEdges; + bool hasEntryBlock = false; + + for (const auto& block : topology.basicBlocks) + { + auto boundary = std::upper_bound(blockBoundaries.begin(), blockBoundaries.end(), block.start); + Address segmentStart = block.start; + BasicBlock* previousBlock = nullptr; + while (segmentStart < block.end) + { + const Address segmentEnd = boundary != blockBoundaries.end() && *boundary < block.end + ? *boundary + : block.end; + auto instruction = GetInstruction(&m_impl->m_instructions, segmentStart); + BasicBlockInstructions blockInstructions; + while (instruction != m_impl->m_instructions.end() && instruction->GetAddress() < segmentEnd) + { + blockInstructions.AddInstruction(instruction); + ++instruction; + } + + BasicBlock* basicBlock = BasicBlock::Find(segmentStart); + if (!basicBlock) + basicBlock = BasicBlock::Create(&blockInstructions); + if (basicBlock) + { + function->AddBasicBlock(basicBlock); + hasEntryBlock |= basicBlock->GetEntryPoint() == topology.entryPoint; + if (previousBlock) + edges.emplace_back(previousBlock->GetLastAddress(), basicBlock->GetEntryPoint(), + FlowGraphEdge::TYPE_UNCONDITIONAL); + previousBlock = basicBlock; + } + + segmentStart = segmentEnd; + if (boundary != blockBoundaries.end() && *boundary == segmentEnd) + ++boundary; + } + } + + std::sort(edges.begin(), edges.end()); + edges.erase(std::unique(edges.begin(), edges.end()), edges.end()); + for (const auto& edge : edges) + function->AddEdge(edge); + function->SortGraph(); + size_t instructionCount = 0; + for (const auto* basicBlock : function->GetBasicBlocks()) + instructionCount += basicBlock->GetInstructionCount(); + + if (!hasEntryBlock || function->GetBasicBlocks().size() >= FlowGraph::kMaxFunctionBasicBlocks + || function->GetEdges().size() >= FlowGraph::kMaxFunctionEdges + || instructionCount >= FlowGraph::kMaxFunctionInstructions) + { + function->Clear(); + function->SetType(Function::TYPE_INVALID); + } + else + { + for (const auto& [source, target] : topology.callEdges) + m_impl->m_callGraph->ScheduleEdgeAdd(function.get(), source, target); + } + + const bool inserted = functions.emplace(topology.entryPoint, function.get()).second; + assert(inserted); + (void)inserted; + function.release(); + } + + for (const Address entryPoint : m_impl->m_callGraph->GetFunctions()) + { + if (functions.find(entryPoint) == functions.end()) + functions.emplace(entryPoint, new Function(entryPoint)); + } + m_impl->m_callGraph->CommitEdges(); + if (!progress(0.65)) + return false; + + m_impl->m_flowGraph->PruneFlowGraphEdges(); + m_impl->m_callGraph->PostProcessComments(); + if (!progress(0.7)) + return false; + + size_t functionIndex = 0; + for (const auto& [address, function] : functions) + { + if (((functionIndex++ & 0x3ff) == 0) + && !progress(0.7 + (0.1 * static_cast(functionIndex) / functions.size()))) + return false; + BinaryNinja::Ref funcSym = m_impl->m_view->GetSymbolByAddress(address); + if (!funcSym) + funcSym = new BinaryNinja::Symbol(FunctionSymbol, fmt::format("func_{:x}", address), address); + function->SetName(funcSym->GetRawName(), funcSym->GetShortName()); + + if (funcSym->GetType() == ImportedFunctionSymbol) + { + function->SetType(Function::TYPE_IMPORTED); + } + else if (function->GetType() == Function::TYPE_NONE || function->GetTypeHeuristic() == Function::TYPE_STANDARD) + { + if (function->GetBasicBlocks().empty()) + function->SetType(Function::TYPE_IMPORTED); + else + function->SetType(Function::TYPE_STANDARD); + } + } + + const std::string placeholderSha256(64, '0'); + const auto architecture = m_impl->m_view->GetDefaultArchitecture(); + const std::string architectureName = architecture ? architecture->GetName() : "unknown"; + + BinExport2Writer writer( + exportFilePath, m_impl->m_view->GetFile()->GetOriginalFilename(), placeholderSha256, architectureName); + if (!progress(0.85)) + return false; + + auto status = writer.Write(*m_impl->m_callGraph, *m_impl->m_flowGraph, m_impl->m_instructions, + m_impl->m_addressReferences, m_impl->m_addressSpace); + + if (!status.ok()) + { + BinaryNinja::LogErrorF("Failed to export BinDiff data: {}", status.message()); + return false; + } + return progress(1.0); +} diff --git a/plugins/bindiff_similarity/processor.h b/plugins/bindiff_similarity/processor.h new file mode 100644 index 0000000000..f86e808544 --- /dev/null +++ b/plugins/bindiff_similarity/processor.h @@ -0,0 +1,24 @@ +#pragma once + +#include "binaryninjaapi.h" +#include +#include +#include + +class BinDiffProcessor +{ +public: + struct Impl; + std::unique_ptr m_impl; + + BinDiffProcessor(BinaryNinja::BinaryView& view); + + ~BinDiffProcessor(); + + void AddFunction(BinaryNinja::Function& func); + + // Finalizes the graphs and writes the .BinExport protobuf to the specified path. + // Returns false when the output could not be written. + bool Process(const std::string& exportFilePath, + std::function progress = [](double) { return true; }); +}; diff --git a/plugins/bindiff_similarity/provider.cpp b/plugins/bindiff_similarity/provider.cpp new file mode 100644 index 0000000000..c83a075e38 --- /dev/null +++ b/plugins/bindiff_similarity/provider.cpp @@ -0,0 +1,344 @@ +#include "provider.h" + +#include "third_party/zynamics/bindiff/differ.h" +#include "third_party/zynamics/bindiff/match/context.h" +#include "third_party/zynamics/bindiff/match/call_graph.h" + +#include +#include +#include +#include +#include + +using namespace BinaryNinja; + +namespace { + // TODO: Hilarious windows esq progress, will remove at some point but that can be done later. + constexpr double ViewPreparationProgress = 0.4; + constexpr double MatchingProgress = 0.59; + + SimilarityRangeAnnotation BlockAnnotation(Function& function, uint64_t address, BNSimilarityAnnotationType type) + { + const Ref block = function.GetBasicBlockAtAddress(function.GetArchitecture(), address); + return {address, block ? block->GetEnd() : address + 1, type}; + } + + std::string GetFlowGraphName(const security::bindiff::FlowGraph& graph) + { + if (!graph.GetDemangledName().empty()) + return graph.GetDemangledName(); + return graph.GetName(); + } + + struct ProgressRange + { + double start; + double length; + + double operator()(const double progress) const { return start + (length * std::clamp(progress, 0.0, 1.0)); } + }; + + ProgressRange EdgeProgressRange(SimilaritySessionNode& from, SimilaritySessionNode& to) + { + const auto incoming = to.GetIncomingEdges(); + const auto edge = std::ranges::lower_bound(incoming, from.GetId()); + const size_t edgeIndex = static_cast(std::distance(incoming.begin(), edge)); + const double edgeLength = MatchingProgress / static_cast(incoming.size()); + return {ViewPreparationProgress + (edgeLength * static_cast(edgeIndex)), edgeLength}; + } +} // namespace + +Ref GoogleSimilarityProviderType::Create(Settings&) +{ + return new GoogleSimilarityProvider(this); +} + +Ref GoogleSimilarityProviderType::GetDefaultSettings() +{ + return Settings::Instance("googleSimilarityProvider"); +} + +GoogleSimilarityProvider::GoogleSimilarityProvider(SimilarityProviderType* type) : SimilarityProvider(type) {} + +bool GoogleSimilarityProvider::VisitNodeEdge(SimilaritySessionNode& from, SimilaritySessionNode& to, + SimilarityProviderResults& results, SimilaritySessionCompletion& completion) +{ + std::shared_ptr primaryState; + std::shared_ptr secondaryState; + { + std::shared_lock viewLock(m_viewsMutex); + const auto primary = m_views.find(to.GetId()); + const auto secondary = m_views.find(from.GetId()); + if ((primary == m_views.end()) || (secondary == m_views.end())) + return true; + primaryState = primary->second; + secondaryState = secondary->second; + } + + const SimilaritySessionCompletionQuery progressQuery {to.GetId(), GetId(), std::nullopt}; + const ProgressRange edgeProgress = EdgeProgressRange(from, to); + if (completion.IsStopRequested()) + return false; + completion.SetProgress(progressQuery, edgeProgress(0.0)); + + const auto scheduled = to.GetScheduledEntities(); + const std::unordered_set scheduledEntities(scheduled.begin(), scheduled.end()); + + std::vector pendingMatches; + { + std::scoped_lock viewLocks(primaryState->mutex, secondaryState->mutex); + BinDiffView& primaryView = *primaryState->view; + BinDiffView& secondaryView = *secondaryState->view; + security::bindiff::ResetMatches(&primaryView.m_flowGraphs); + security::bindiff::ResetMatches(&secondaryView.m_flowGraphs); + struct MatchReset + { + security::bindiff::FlowGraphs& primary; + security::bindiff::FlowGraphs& secondary; + ~MatchReset() + { + security::bindiff::ResetMatches(&primary); + security::bindiff::ResetMatches(&secondary); + } + } reset {primaryView.m_flowGraphs, secondaryView.m_flowGraphs}; + + security::bindiff::FixedPoints fixedPoints; + security::bindiff::MatchingContext context(primaryView.m_callGraph, secondaryView.m_callGraph, + primaryView.m_flowGraphs, secondaryView.m_flowGraphs, fixedPoints); + + // TODO: Expose options for diffing. + security::bindiff::Diff(&context, security::bindiff::GetDefaultMatchingSteps(), + security::bindiff::GetDefaultMatchingStepsBasicBlock()); + if (completion.IsStopRequested()) + return false; + completion.SetProgress(progressQuery, edgeProgress(0.7)); + pendingMatches.reserve(fixedPoints.size()); + + size_t fixedPointIndex = 0; + for (const auto& fixedPoint : fixedPoints) + { + if (completion.IsStopRequested()) + return false; + const double similarity = fixedPoint.GetSimilarity(); + // Scaling here because we choose to keep the scoring and confidence in a 0-255 range. + const double normalizedSimilarity = std::isfinite(similarity) ? std::clamp(similarity, 0.0, 1.0) : 0.0; + const auto similarityScore = static_cast(std::lround(normalizedSimilarity * 255)); + const Address primaryAddr = fixedPoint.GetPrimary()->GetEntryPointAddress(); + const Address secondaryAddr = fixedPoint.GetSecondary()->GetEntryPointAddress(); + const std::string primaryName = GetFlowGraphName(*fixedPoint.GetPrimary()); + const std::string secondaryName = GetFlowGraphName(*fixedPoint.GetSecondary()); + + const SimilarityEntityId primaryId = + to.CreateEntity({SimilarityEntityFunction, primaryAddr, primaryName}); + fixedPointIndex++; + const double fixedPointProgress = static_cast(fixedPointIndex) / fixedPoints.size(); + completion.SetProgress(progressQuery, edgeProgress(0.7 + (0.25 * fixedPointProgress))); + if (!scheduledEntities.contains(primaryId)) + continue; + const SimilarityEntityId secondaryId = + from.CreateEntity({SimilarityEntityFunction, secondaryAddr, secondaryName}); + const SimilarityEntityRef primaryRef(to.GetId(), primaryId); + const SimilarityEntityRef secondaryRef(from.GetId(), secondaryId); + Ref primaryFunction = to.GetEntityFunction(primaryId); + Ref secondaryFunction = from.GetEntityFunction(secondaryId); + std::vector primaryBlocks; + std::vector secondaryBlocks; + for (const auto& blockMatch : fixedPoint.GetBasicBlockFixedPoints()) + { + const auto primaryInstructions = + fixedPoint.GetPrimary()->GetInstructions(blockMatch.GetPrimaryVertex()); + const auto secondaryInstructions = + fixedPoint.GetSecondary()->GetInstructions(blockMatch.GetSecondaryVertex()); + const size_t matchedInstructions = blockMatch.GetInstructionMatches().size(); + const size_t primaryInstructionCount = primaryInstructions.second - primaryInstructions.first; + const size_t secondaryInstructionCount = secondaryInstructions.second - secondaryInstructions.first; + primaryBlocks.push_back({fixedPoint.GetPrimary()->GetAddress(blockMatch.GetPrimaryVertex()), + primaryInstructionCount, secondaryInstructionCount, matchedInstructions}); + secondaryBlocks.push_back({fixedPoint.GetSecondary()->GetAddress(blockMatch.GetSecondaryVertex()), + secondaryInstructionCount, primaryInstructionCount, matchedInstructions}); + } + if (primaryFunction && secondaryFunction) + { + const auto& primaryGraph = fixedPoint.GetPrimary()->GetGraph(); + for (auto [vertex, end] = boost::vertices(primaryGraph); vertex != end; ++vertex) + { + if (!fixedPoint.GetPrimary()->GetFixedPoint(*vertex)) + primaryBlocks.push_back({fixedPoint.GetPrimary()->GetAddress(*vertex), 0, 0, std::nullopt}); + } + const auto& secondaryGraph = fixedPoint.GetSecondary()->GetGraph(); + for (auto [vertex, end] = boost::vertices(secondaryGraph); vertex != end; ++vertex) + { + if (!fixedPoint.GetSecondary()->GetFixedPoint(*vertex)) + secondaryBlocks.push_back({fixedPoint.GetSecondary()->GetAddress(*vertex), 0, 0, std::nullopt}); + } + } + + // TODO: Move this entirely into the render method, doing this will require us to do some more coordination but + // TODO: it will definitely be cleaner and allow us to remove the above and below code. + std::optional primaryRender; + std::optional secondaryRender; + if (primaryFunction && secondaryFunction) + { + primaryRender = RenderData {{{SimilarityAnnotationAdded, std::move(primaryBlocks)}, + {SimilarityAnnotationRemoved, secondaryBlocks}}}; + secondaryRender = RenderData {{{SimilarityAnnotationRemoved, std::move(secondaryBlocks)}, + {SimilarityAnnotationAdded, primaryRender->sources[0].blocks}}}; + } + pendingMatches.push_back({{primaryRef, secondaryRef}, similarityScore, primaryName, secondaryName, + std::move(primaryRender), std::move(secondaryRender)}); + } + if (fixedPoints.empty()) + completion.SetProgress(progressQuery, edgeProgress(0.95)); + } + + for (const auto& match : pendingMatches) + { + const SimilarityResultId primaryResult = + results.AddResult(match.key.primary, match.key.secondary, match.similarity, 255); + const SimilarityResultId secondaryResult = + results.AddResult(match.key.secondary, match.key.primary, match.similarity, 255); + if (!primaryResult.Value() || !secondaryResult.Value()) + return false; + } + if (completion.IsStopRequested()) + return false; + completion.SetProgress(progressQuery, edgeProgress(0.99)); + + { + std::lock_guard lock(m_dataMutex); + std::erase_if(m_renderData, [&](const auto& entry) { + const MatchKey& key = entry.first; + if ((key.primary.nodeId == to.GetId()) && (key.secondary.nodeId == from.GetId())) + return scheduledEntities.contains(key.primary.entityId); + if ((key.primary.nodeId == from.GetId()) && (key.secondary.nodeId == to.GetId())) + return scheduledEntities.contains(key.secondary.entityId); + return false; + }); + for (auto& match : pendingMatches) + { + m_entityNames.insert_or_assign(match.key.primary, std::move(match.primaryName)); + m_entityNames.insert_or_assign(match.key.secondary, std::move(match.secondaryName)); + if (match.primaryRender) + m_renderData.insert_or_assign(match.key, std::move(*match.primaryRender)); + else + m_renderData.erase(match.key); + const MatchKey reverseKey {match.key.secondary, match.key.primary}; + if (match.secondaryRender) + m_renderData.insert_or_assign(reverseKey, std::move(*match.secondaryRender)); + else + m_renderData.erase(reverseKey); + } + } + return true; +} + +bool GoogleSimilarityProvider::VisitNode( + SimilaritySessionNode& node, SimilarityProviderResults&, SimilaritySessionCompletion& completion) +{ + const auto progressQuery = SimilaritySessionCompletionQuery::ForNode(node.GetId()).WithProvider(GetId()); + const double progressScale = node.GetIncomingEdges().empty() ? 0.99 : ViewPreparationProgress; + auto diffView = BinDiffView::FromSessionNode(node, [&](double progress) { + completion.SetProgress(progressQuery, progressScale * progress); + return !completion.IsStopRequested(); + }); + if (completion.IsStopRequested()) + return false; + if (!diffView) + { + std::lock_guard lock(m_viewsMutex); + m_views.erase(node.GetId()); + return true; + } + auto state = std::make_shared(); + state->view = std::shared_ptr(std::move(diffView)); + + { + std::lock_guard lock(m_viewsMutex); + m_views.insert_or_assign(node.GetId(), std::move(state)); + } + return true; +} + +std::optional> GoogleSimilarityProvider::FindResult( + SimilaritySessionNode& node, SimilarityEntityId entity, SimilarityResultId result) +{ + const auto value = node.GetResult(result); + if (!value || (value->providerId != GetId())) + return std::nullopt; + return std::make_pair(SimilarityEntityRef(node.GetId(), entity), *value); +} + +Ref GoogleSimilarityProvider::FindNode(SimilaritySessionNode& node, SimilaritySessionNodeId id) +{ + if (id == node.GetId()) + return &node; + for (const auto& candidate : node.GetIncomingNodes()) + { + if (candidate->GetId() == id) + return candidate; + } + for (const auto& candidate : node.GetOutgoingNodes()) + { + if (candidate->GetId() == id) + return candidate; + } + return nullptr; +} + +std::optional GoogleSimilarityProvider::GetName( + SimilaritySessionNode& node, SimilarityEntityId entity, SimilarityResultId result) +{ + const auto match = FindResult(node, entity, result); + if (!match) + return std::nullopt; + + std::shared_lock lock(m_dataMutex); + const auto name = m_entityNames.find(match->second.target); + if ((name == m_entityNames.end()) || name->second.empty()) + return std::nullopt; + return name->second; +} + +void GoogleSimilarityProvider::Render( + SimilaritySessionNode& node, SimilarityEntityId entity, SimilarityRenderContext& context, SimilarityResultId result) +{ + const auto match = FindResult(node, entity, result); + if (!match) + return; + const Ref targetNode = FindNode(node, match->second.target.nodeId); + if (!targetNode) + return; + const std::vector> functions { + node.GetEntityFunction(match->first.entityId), targetNode->GetEntityFunction(match->second.target.entityId)}; + const std::array entities {match->first, match->second.target}; + + RenderData data; + { + std::shared_lock lock(m_dataMutex); + const auto entry = m_renderData.find(MatchKey {match->first, match->second.target}); + if (entry == m_renderData.end()) + return; + data = entry->second; + } + for (size_t i = 0; i < data.sources.size() && i < functions.size(); i++) + { + const auto& source = data.sources[i]; + const auto& function = functions[i]; + if (!function) + continue; + Ref renderer = new DiffRenderer(); + for (const auto& block : source.blocks) + { + BNSimilarityAnnotationType type = source.unmatchedType; + if (block.matchedInstructionCount) + { + if ((block.instructionCount == *block.matchedInstructionCount) + && (block.otherInstructionCount == *block.matchedInstructionCount)) + continue; + type = SimilarityAnnotationChanged; + } + renderer->AddRangeAnnotation(BlockAnnotation(*function, block.address, type)); + } + renderer->Render(context, *function, entities[i]); + } +} diff --git a/plugins/bindiff_similarity/provider.h b/plugins/bindiff_similarity/provider.h new file mode 100644 index 0000000000..61efa39bae --- /dev/null +++ b/plugins/bindiff_similarity/provider.h @@ -0,0 +1,103 @@ +#pragma once + +#include "binaryninjaapi.h" +#include "diffview.h" + +#include +#include + +class GoogleSimilarityProviderType : public BinaryNinja::SimilarityProviderType +{ +public: + GoogleSimilarityProviderType() : + SimilarityProviderType("Google BinDiff", "Uses Google's BinDiff project to perform binary similarity") + {} + + BinaryNinja::Ref Create(BinaryNinja::Settings& settings) override; + + BinaryNinja::Ref GetDefaultSettings() override; +}; + +class GoogleSimilarityProvider : public BinaryNinja::SimilarityProvider +{ + struct ViewState + { + std::mutex mutex; + std::shared_ptr view; + }; + + struct MatchKey + { + BinaryNinja::SimilarityEntityRef primary; + BinaryNinja::SimilarityEntityRef secondary; + + bool operator==(const MatchKey&) const = default; + }; + + struct RenderData + { + struct BlockComparison + { + uint64_t address; + size_t instructionCount; + size_t otherInstructionCount; + std::optional matchedInstructionCount; + }; + + struct Source + { + BNSimilarityAnnotationType unmatchedType; + std::vector blocks; + }; + + std::vector sources; + }; + + struct PendingMatch + { + MatchKey key; + uint8_t similarity; + std::string primaryName; + std::string secondaryName; + std::optional primaryRender; + std::optional secondaryRender; + }; + + struct MatchKeyHash + { + size_t operator()(const MatchKey& key) const + { + const size_t primaryHash = std::hash()(key.primary); + const size_t secondaryHash = std::hash()(key.secondary); + return primaryHash ^ (secondaryHash + 0x9e3779b9 + (primaryHash << 6) + (primaryHash >> 2)); + } + }; + + std::shared_mutex m_viewsMutex; + std::unordered_map> m_views; + + std::shared_mutex m_dataMutex; + std::unordered_map m_entityNames; + std::unordered_map m_renderData; + + std::optional> FindResult( + BinaryNinja::SimilaritySessionNode& node, BinaryNinja::SimilarityEntityId entity, + BinaryNinja::SimilarityResultId result); + static BinaryNinja::Ref FindNode( + BinaryNinja::SimilaritySessionNode& node, BinaryNinja::SimilaritySessionNodeId id); + +public: + explicit GoogleSimilarityProvider(BinaryNinja::SimilarityProviderType* type); + + bool VisitNode(BinaryNinja::SimilaritySessionNode& node, BinaryNinja::SimilarityProviderResults& results, + BinaryNinja::SimilaritySessionCompletion& completion) override; + + bool VisitNodeEdge(BinaryNinja::SimilaritySessionNode& from, BinaryNinja::SimilaritySessionNode& to, + BinaryNinja::SimilarityProviderResults& results, BinaryNinja::SimilaritySessionCompletion& completion) override; + + std::optional GetName(BinaryNinja::SimilaritySessionNode& node, BinaryNinja::SimilarityEntityId entity, + BinaryNinja::SimilarityResultId result) override; + + void Render(BinaryNinja::SimilaritySessionNode& node, BinaryNinja::SimilarityEntityId entity, + BinaryNinja::SimilarityRenderContext& context, BinaryNinja::SimilarityResultId result) override; +}; diff --git a/python/examples/bindiff.py b/python/examples/bindiff.py new file mode 100644 index 0000000000..5a0abd3927 --- /dev/null +++ b/python/examples/bindiff.py @@ -0,0 +1,79 @@ +"""Compare two binaries with the Google BinDiff similarity provider (Ultimate only).""" + +import argparse +import csv +import time +from pathlib import Path + +from binaryninja import ( + BinaryView, + LogLevel, + SimilarityProviderType, + SimilaritySession, + SimilaritySessionNode, + load, +) +from binaryninja.log import log_to_stdout +from binaryninja.similarity import SimilarityEntityInfo, SimilarityEntityType, SimilaritySessionCompletion + + +def wait_for_completion(completion: SimilaritySessionCompletion, timeout: float = 120) -> bool: + """Wait up to ``timeout`` seconds for a similarity run to finish.""" + deadline = time.monotonic() + timeout + while not completion.is_finished and time.monotonic() < deadline: + time.sleep(0.1) + return completion.is_finished + + +def diff(primary: BinaryView, secondary: BinaryView, output_csv: Path) -> int: + """Compare two views and write their function matches to ``output_csv``.""" + provider_type = SimilarityProviderType["Google BinDiff"] + provider_settings = provider_type.get_default_settings() + provider = provider_type.create(provider_settings) + + session = SimilaritySession() + session.add_provider(provider) + primary_node = SimilaritySessionNode(primary) + secondary_node = SimilaritySessionNode(secondary) + session.graph.add_node(primary_node) + session.graph.add_node(secondary_node) + session.graph.add_edge(primary_node, secondary_node) + + completion = session.run() + if not wait_for_completion(completion): + completion.request_stop() + wait_for_completion(completion, timeout=10) + raise TimeoutError("BinDiff did not finish before the timeout") + + match_count = 0 + with output_csv.open("w", newline="") as output: + writer = csv.writer(output) + writer.writerow(["Address", "Function", "Matched Function", "Similarity", "Confidence"]) + for function in secondary.functions: + entity = secondary_node.create_entity( + SimilarityEntityInfo(SimilarityEntityType.SimilarityEntityFunction, function.start, function.name) + ) + for result_id in secondary_node.get_results(entity): + result = secondary_node.get_result(result_id) + name = provider.get_name(secondary_node, entity, result_id) + writer.writerow([hex(function.start), function.name, name, result.similarity, result.confidence]) + match_count += 1 + return match_count + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("primary", help="baseline binary") + parser.add_argument("secondary", help="binary to compare") + parser.add_argument("output", type=Path, help="output CSV path") + args = parser.parse_args() + + log_to_stdout(LogLevel.WarningLog) + with load(args.primary) as primary, load(args.secondary) as secondary: + count = diff(primary, secondary, args.output) + print(f"Wrote {count} matches to {args.output}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/similarity.cpp b/similarity.cpp new file mode 100644 index 0000000000..5014909680 --- /dev/null +++ b/similarity.cpp @@ -0,0 +1,1368 @@ +#include + +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + +namespace { + template + Result RunSimilarityCallback(const char* message, Result failure, Callback&& callback) noexcept + { + try + { + return callback(); + } + catch (const std::exception& exception) + { + LogErrorForException(exception, "%s", message); + } + catch (...) + { + LogError("%s", message); + } + return failure; + } + + template + void RunSimilarityCallback(const char* message, Callback&& callback) noexcept + { + try + { + callback(); + } + catch (const std::exception& exception) + { + LogErrorForException(exception, "%s", message); + } + catch (...) + { + LogError("%s", message); + } + } +} // namespace + +SimilarityView::SimilarityView(BNSimilarityView* view) +{ + m_object = view; +} + +string SimilarityView::GetGroup() const +{ + char* group = BNSimilarityViewGetGroup(m_object); + string result = group ? group : ""; + BNFreeString(group); + return result; +} + +BNSimilarityViewType SimilarityView::GetType() const +{ + return BNSimilarityViewGetType(m_object); +} + +Ref SimilarityView::GetFlowGraph() const +{ + BNFlowGraph* graph = BNSimilarityViewGetFlowGraph(m_object); + return graph ? Ref(new FlowGraph(graph)) : nullptr; +} + +Ref SimilarityView::GetLinearViewData() const +{ + BNBinaryView* data = BNSimilarityViewGetLinearViewData(m_object); + return data ? Ref(new BinaryView(data)) : nullptr; +} + +Ref SimilarityView::GetLinearView() const +{ + BNLinearViewObject* linearView = BNSimilarityViewGetLinearView(m_object); + return linearView ? Ref(new LinearViewObject(linearView)) : nullptr; +} + +optional SimilarityView::GetEntity() const +{ + BNSimilarityEntityRef entity; + if (!BNSimilarityViewGetEntity(m_object, &entity)) + return nullopt; + return SimilarityEntityRef(entity); +} + +SimilarityRenderContext::SimilarityRenderContext() +{ + m_object = BNCreateSimilarityRenderContext(); +} + +SimilarityRenderContext::SimilarityRenderContext(BNSimilarityRenderContext* context) +{ + m_object = context; +} + +void SimilarityRenderContext::SetPreferredViewType(const FunctionViewType& type) +{ + BNSimilarityRenderContextSetPreferredViewType(m_object, type.ToAPIObject()); +} + +FunctionViewType SimilarityRenderContext::GetPreferredViewType() const +{ + const BNFunctionGraphType type = BNSimilarityRenderContextGetPreferredViewType(m_object); + if (type != HighLevelLanguageRepresentationFunctionGraph) + return FunctionViewType(type); + char* name = BNSimilarityRenderContextGetPreferredViewTypeName(m_object); + FunctionViewType result {string(name)}; + BNFreeString(name); + return result; +} + +void SimilarityRenderContext::AddFlowGraph(const string& group, FlowGraph& graph) +{ + BNSimilarityRenderContextAddFlowGraph(m_object, group.c_str(), graph.GetObject()); +} + +void SimilarityRenderContext::AddFlowGraph(const string& group, FlowGraph& graph, const SimilarityEntityRef& entity) +{ + const BNSimilarityEntityRef rawEntity = entity.ToRaw(); + BNSimilarityRenderContextAddFlowGraphForEntity(m_object, group.c_str(), graph.GetObject(), &rawEntity); +} + +void SimilarityRenderContext::AddLinearView(const string& group, BinaryView& data, LinearViewObject& linearView) +{ + BNSimilarityRenderContextAddLinearView(m_object, group.c_str(), data.GetObject(), linearView.GetObject()); +} + +void SimilarityRenderContext::AddLinearView( + const string& group, BinaryView& data, LinearViewObject& linearView, const SimilarityEntityRef& entity) +{ + const BNSimilarityEntityRef rawEntity = entity.ToRaw(); + BNSimilarityRenderContextAddLinearViewForEntity( + m_object, group.c_str(), data.GetObject(), linearView.GetObject(), &rawEntity); +} + +vector> SimilarityRenderContext::GetViews() const +{ + size_t count = 0; + BNSimilarityView** views = BNGetSimilarityRenderContextViews(m_object, &count); + vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new SimilarityView(BNNewSimilarityViewReference(views[i]))); + BNFreeSimilarityViewList(views, count); + return result; +} + +DiffRenderer::DiffRenderer() +{ + m_object = BNCreateDiffRenderer(); +} + +DiffRenderer::DiffRenderer(BNDiffRenderer* renderer) +{ + m_object = renderer; +} + +void DiffRenderer::AddRangeAnnotation(const SimilarityRangeAnnotation& annotation) +{ + AddRangeAnnotation(annotation.start, annotation.end, annotation.type); +} + +void DiffRenderer::AddRangeAnnotation(uint64_t start, uint64_t end, BNSimilarityAnnotationType type) +{ + BNDiffRendererAddRangeAnnotation(m_object, start, end, type); +} + +void DiffRenderer::Render(SimilarityRenderContext& context, Function& function) +{ + BNDiffRendererRenderFunction(m_object, context.GetObject(), function.GetObject()); +} + +void DiffRenderer::Render(SimilarityRenderContext& context, Function& function, const SimilarityEntityRef& entity) +{ + const BNSimilarityEntityRef rawEntity = entity.ToRaw(); + BNDiffRendererRenderFunctionForEntity(m_object, context.GetObject(), function.GetObject(), &rawEntity); +} + +void DiffRenderer::Render(SimilarityRenderContext& context, const string& group, FlowGraph& graph) +{ + BNDiffRendererRenderFlowGraph(m_object, context.GetObject(), group.c_str(), graph.GetObject()); +} + +void DiffRenderer::Render( + SimilarityRenderContext& context, const string& group, FlowGraph& graph, const SimilarityEntityRef& entity) +{ + const BNSimilarityEntityRef rawEntity = entity.ToRaw(); + BNDiffRendererRenderFlowGraphForEntity(m_object, context.GetObject(), group.c_str(), graph.GetObject(), &rawEntity); +} + +void DiffRenderer::Render( + SimilarityRenderContext& context, const string& group, BinaryView& data, LinearViewObject& linearView) +{ + BNDiffRendererRenderLinearView( + m_object, context.GetObject(), group.c_str(), data.GetObject(), linearView.GetObject()); +} + +void DiffRenderer::Render(SimilarityRenderContext& context, const string& group, BinaryView& data, + LinearViewObject& linearView, const SimilarityEntityRef& entity) +{ + const BNSimilarityEntityRef rawEntity = entity.ToRaw(); + BNDiffRendererRenderLinearViewForEntity( + m_object, context.GetObject(), group.c_str(), data.GetObject(), linearView.GetObject(), &rawEntity); +} + +SimilarityResultId SimilarityProviderResults::AddResult( + const SimilarityEntityRef& source, const SimilarityEntityRef& target, uint8_t similarity, uint8_t confidence) +{ + const BNSimilarityEntityRef rawSource = source.ToRaw(); + const BNSimilarityEntityRef rawTarget = target.ToRaw(); + return BNSimilarityProviderResultsAddResult(m_object, &rawSource, &rawTarget, similarity, confidence); +} + + +SimilarityProviderType::SimilarityProviderType(string name, string description) : + m_nameForRegister(std::move(name)), m_descForRegister(std::move(description)) +{} + + +SimilarityProviderType::SimilarityProviderType(BNSimilarityProviderType* type) +{ + m_object = type; +} + + +void SimilarityProviderType::Register(SimilarityProviderType* type) +{ + BNCustomSimilarityProviderType cb {}; + cb.context = type; + cb.create = CreateCallback; + cb.getDefaultSettings = GetDefaultSettingsCallback; + + type->AddRefForRegistration(); + type->m_object = + BNRegisterSimilarityProviderType(type->m_nameForRegister.c_str(), type->m_descForRegister.c_str(), &cb); +} + + +vector> SimilarityProviderType::GetList() +{ + size_t count; + BNSimilarityProviderType** list = BNGetSimilarityProviderTypeList(&count); + vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new CoreSimilarityProviderType(list[i])); + BNFreeSimilarityProviderTypeList(list); + return result; +} + + +Ref SimilarityProviderType::GetByName(const string& name) +{ + BNSimilarityProviderType* result = BNGetSimilarityProviderTypeByName(name.c_str()); + if (!result) + return nullptr; + return new CoreSimilarityProviderType(result); +} + + +std::string SimilarityProviderType::GetName() const +{ + char* name = BNSimilarityProviderTypeGetName(m_object); + std::string result = name; + BNFreeString(name); + return result; +} + + +std::string SimilarityProviderType::GetDescription() const +{ + char* description = BNSimilarityProviderTypeGetDescription(m_object); + std::string result = description; + BNFreeString(description); + return result; +} + + +CoreSimilarityProviderType::CoreSimilarityProviderType(BNSimilarityProviderType* type) : SimilarityProviderType(type) {} + + +Ref CoreSimilarityProviderType::Create(Settings& settings) +{ + BNSimilarityProvider* provider = BNSimilarityProviderTypeCreateProvider(m_object, settings.m_object); + if (!provider) + return nullptr; + return new CoreSimilarityProvider(provider); +} + +Ref CoreSimilarityProviderType::GetDefaultSettings() +{ + BNSettings* settings = BNSimilarityProviderTypeGetDefaultSettings(m_object); + if (!settings) + return nullptr; + return new Settings(settings); +} + + +SimilarityProvider::SimilarityProvider(BNSimilarityProvider* provider) +{ + m_object = provider; +} + + +Ref SimilarityProvider::GetType() const +{ + return new CoreSimilarityProviderType(BNSimilarityProviderGetType(m_object)); +} + +SimilarityProviderId SimilarityProvider::GetId() const +{ + return BNSimilarityProviderGetId(m_object); +} + + +SimilarityProvider::SimilarityProvider(SimilarityProviderType* type) +{ + BNCustomSimilarityProvider cb {}; + cb.context = this; + cb.updateSettings = UpdateSettingsCallback; + cb.visitNode = VisitNodeCallback; + cb.visitNodeEdge = VisitNodeEdgeCallback; + cb.getName = GetNameCallback; + cb.apply = ApplyCallback; + cb.render = RenderCallback; + cb.free = FreeContextCallback; + + AddRefForRegistration(); + m_object = BNCreateCustomSimilarityProvider(type->m_object, &cb); +} + + +CoreSimilarityProvider::CoreSimilarityProvider(BNSimilarityProvider* provider) : SimilarityProvider(provider) {} + +bool SimilarityProvider::UpdateSettingsCallback(void* ctxt, BNSettings* settings) +{ + return RunSimilarityCallback("Unhandled exception updating similarity provider settings", false, [&]() { + CallbackRef provider(ctxt); + Ref settingsObj = new Settings(BNNewSettingsReference(settings)); + return provider->UpdateSettings(*settingsObj); + }); +} + +BNSimilarityApplyStatus SimilarityProvider::Apply( + SimilaritySessionNode& node, SimilarityEntityId entity, SimilarityResultId resultId) +{ + const auto result = node.GetResult(resultId); + if (!result) + return SimilarityApplyFailed; + const auto rawTarget = result->target.ToRaw(); + return BNSimilaritySessionNodeApplyTarget(node.GetObject(), entity, &rawTarget); +} + + +void SimilarityProvider::VisitNode(SimilaritySessionNode& node, SimilaritySessionCompletion& completion) +{ + BNSimilarityProviderVisitNode(m_object, node.GetObject(), completion.GetObject()); +} + +void SimilarityProvider::VisitNodeEdge( + SimilaritySessionNode& from, SimilaritySessionNode& to, SimilaritySessionCompletion& completion) +{ + BNSimilarityProviderVisitNodeEdge(m_object, from.GetObject(), to.GetObject(), completion.GetObject()); +} + +bool CoreSimilarityProvider::VisitNode( + SimilaritySessionNode& node, SimilarityProviderResults& results, SimilaritySessionCompletion& completion) +{ + return BNSimilarityProviderPerformVisitNode( + m_object, node.GetObject(), results.GetObject(), completion.GetObject()); +} + +bool CoreSimilarityProvider::VisitNodeEdge(SimilaritySessionNode& from, SimilaritySessionNode& to, + SimilarityProviderResults& results, SimilaritySessionCompletion& completion) +{ + return BNSimilarityProviderPerformVisitNodeEdge( + m_object, from.GetObject(), to.GetObject(), results.GetObject(), completion.GetObject()); +} + + +std::optional CoreSimilarityProvider::GetName( + SimilaritySessionNode& node, SimilarityEntityId entity, SimilarityResultId resultId) +{ + char* name = BNSimilarityProviderGetName(m_object, node.GetObject(), entity, resultId); + if (!name) + return std::nullopt; + std::string result = name; + BNFreeString(name); + return result; +} + + +BNSimilarityApplyStatus CoreSimilarityProvider::Apply(SimilaritySessionNode& node, SimilarityEntityId entity, + SimilarityResultId result) +{ + return BNSimilarityProviderApply(m_object, node.GetObject(), entity, result); +} + + +void CoreSimilarityProvider::Render(SimilaritySessionNode& node, SimilarityEntityId entity, + SimilarityRenderContext& context, SimilarityResultId result) +{ + BNSimilarityProviderRender(m_object, node.GetObject(), entity, context.GetObject(), result); +} + + +SimilaritySessionResolverType::SimilaritySessionResolverType(std::string name, std::string description) : + m_nameForRegister(std::move(name)), m_descForRegister(std::move(description)) +{} + + +SimilaritySessionResolverType::SimilaritySessionResolverType(BNSimilaritySessionResolverType* type) +{ + m_object = type; +} + + +void SimilaritySessionResolverType::Register(SimilaritySessionResolverType* type) +{ + BNCustomSimilaritySessionResolverType cb {}; + cb.context = type; + cb.create = CreateCallback; + cb.getDefaultSettings = GetDefaultSettingsCallback; + + type->AddRefForRegistration(); + type->m_object = + BNRegisterSimilaritySessionResolverType(type->m_nameForRegister.c_str(), type->m_descForRegister.c_str(), &cb); +} + + +std::vector> SimilaritySessionResolverType::GetList() +{ + size_t count; + BNSimilaritySessionResolverType** list = BNGetSimilaritySessionResolverTypeList(&count); + std::vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new CoreSimilaritySessionResolverType(list[i])); + BNFreeSimilaritySessionResolverTypeList(list); + return result; +} + + +Ref SimilaritySessionResolverType::GetByName(const std::string& name) +{ + BNSimilaritySessionResolverType* result = BNGetSimilaritySessionResolverTypeByName(name.c_str()); + if (!result) + return nullptr; + return new CoreSimilaritySessionResolverType(result); +} + + +std::string SimilaritySessionResolverType::GetName() const +{ + char* name = BNSimilaritySessionResolverTypeGetName(m_object); + std::string result = name; + BNFreeString(name); + return result; +} + + +std::string SimilaritySessionResolverType::GetDescription() const +{ + char* description = BNSimilaritySessionResolverTypeGetDescription(m_object); + std::string result = description; + BNFreeString(description); + return result; +} + + +BNSimilaritySessionResolver* SimilaritySessionResolverType::CreateCallback( + void* ctxt, BNSimilaritySession* session, BNSettings* settings) +{ + return RunSimilarityCallback< + BNSimilaritySessionResolver*>("Unhandled exception creating similarity session resolver", nullptr, [&]() { + SimilaritySessionResolverType* type = (SimilaritySessionResolverType*)ctxt; + Ref sessionObj = new SimilaritySession(BNNewSimilaritySessionReference(session)); + Ref settingsObj = new Settings(BNNewSettingsReference(settings)); + Ref result = type->Create(sessionObj, *settingsObj); + if (!result) + return static_cast(nullptr); + return BNNewSimilaritySessionResolverReference(result->GetObject()); + }); +} + + +BNSettings* SimilaritySessionResolverType::GetDefaultSettingsCallback(void* ctxt) +{ + return RunSimilarityCallback< + BNSettings*>("Unhandled exception getting similarity session resolver settings", nullptr, [&]() { + SimilaritySessionResolverType* type = (SimilaritySessionResolverType*)ctxt; + Ref result = type->GetDefaultSettings(); + if (!result) + return static_cast(nullptr); + return BNNewSettingsReference(result->GetObject()); + }); +} + + +CoreSimilaritySessionResolverType::CoreSimilaritySessionResolverType(BNSimilaritySessionResolverType* type) : + SimilaritySessionResolverType(type) +{} + + +Ref CoreSimilaritySessionResolverType::Create( + Ref session, Settings& settings) +{ + BNSimilaritySessionResolver* resolver = + BNSimilaritySessionResolverTypeCreateResolver(m_object, session->GetObject(), settings.GetObject()); + if (!resolver) + return nullptr; + return new CoreSimilaritySessionResolver(resolver); +} + + +Ref CoreSimilaritySessionResolverType::GetDefaultSettings() +{ + BNSettings* settings = BNSimilaritySessionResolverTypeGetDefaultSettings(m_object); + if (!settings) + return nullptr; + return new Settings(settings); +} + + +SimilaritySessionResolver::SimilaritySessionResolver(BNSimilaritySessionResolver* resolver) +{ + m_object = resolver; +} + + +SimilaritySessionResolver::SimilaritySessionResolver( + SimilaritySessionResolverType* type, Ref session) +{ + BNCustomSimilaritySessionResolver cb {}; + cb.context = this; + cb.updateSettings = UpdateSettingsCallback; + cb.prepareForNode = PrepareForNodeCallback; + cb.resolveForNode = ResolveForNodeCallback; + cb.free = FreeContextCallback; + + AddRefForRegistration(); + m_object = BNCreateCustomSimilaritySessionResolver(type->GetObject(), session->GetObject(), &cb); +} + +bool SimilaritySessionResolver::UpdateSettingsCallback(void* ctxt, BNSettings* settings) +{ + return RunSimilarityCallback("Unhandled exception updating similarity resolver settings", false, [&]() { + CallbackRef resolver(ctxt); + Ref settingsObj = new Settings(BNNewSettingsReference(settings)); + return resolver->UpdateSettings(*settingsObj); + }); +} + + +SimilaritySessionResolverId SimilaritySessionResolver::GetId() const +{ + return BNSimilaritySessionResolverGetId(m_object); +} + + +Ref SimilaritySessionResolver::GetType() const +{ + return new CoreSimilaritySessionResolverType(BNSimilaritySessionResolverGetType(m_object)); +} + + +CoreSimilaritySessionResolver::CoreSimilaritySessionResolver(BNSimilaritySessionResolver* resolver) : + SimilaritySessionResolver(resolver) +{} + + +void CoreSimilaritySessionResolver::PrepareForNode( + SimilaritySession& session, SimilaritySessionNode& node, SimilaritySessionCompletion& completion) +{ + BNSimilaritySessionResolverPrepareForNode(m_object, session.GetObject(), node.GetObject(), completion.GetObject()); +} + + +void CoreSimilaritySessionResolver::ResolveForNode(SimilaritySession& session, SimilaritySessionNode& node, + const std::vector& entities, SimilaritySessionCompletion& completion) +{ + std::vector coreEntities; + coreEntities.reserve(entities.size()); + for (const auto entity : entities) + coreEntities.push_back(entity); + BNSimilaritySessionResolverResolveForNode(m_object, session.GetObject(), node.GetObject(), coreEntities.data(), + coreEntities.size(), completion.GetObject()); +} + + +SimilaritySessionNode::SimilaritySessionNode(BNSimilaritySessionNode* node) +{ + m_object = node; +} + + +SimilaritySessionNode::SimilaritySessionNode(Ref view) +{ + m_object = BNCreateSimilaritySessionNode(view->GetObject()); +} + + +SimilaritySessionNode::SimilaritySessionNode(Ref file) +{ + m_object = BNCreateSimilaritySessionNodeFromFile(file->GetObject()); +} + + +Ref SimilaritySessionNode::GetView() const +{ + BNBinaryView* view = BNSimilaritySessionNodeGetView(m_object); + if (!view) + return nullptr; + return new BinaryView(view); +} + + +void SimilaritySessionNode::SetView(Ref view) +{ + BNSimilaritySessionNodeSetView(m_object, view ? view->GetObject() : nullptr); +} + + +Ref SimilaritySessionNode::GetFile() const +{ + return new FileMetadata(BNSimilaritySessionNodeGetFile(m_object)); +} + +Ref SimilaritySessionNode::GetLoadOptions() const +{ + return new Settings(BNSimilaritySessionNodeGetLoadOptions(m_object)); +} + + +SimilaritySessionNodeId SimilaritySessionNode::GetId() const +{ + return BNSimilaritySessionNodeGetId(m_object); +} + + +SimilarityEntityId SimilaritySessionNode::CreateEntity(const SimilarityEntityInfo& info) +{ + auto rawInfo = info.ToRaw(); + return BNSimilaritySessionNodeCreateEntity(m_object, &rawInfo); +} + + +bool SimilaritySessionNode::RemoveEntity(SimilarityEntityId id) +{ + return BNSimilaritySessionNodeRemoveEntity(m_object, id); +} + + +std::optional SimilaritySessionNode::GetEntity(const SimilarityEntityId id) +{ + BNSimilarityEntityInfo result; + if (!BNSimilaritySessionNodeGetEntity(m_object, id, &result)) + return std::nullopt; + SimilarityEntityInfo info(result); + BNFreeSimilarityEntityInfo(&result); + return info; +} + + +std::vector SimilaritySessionNode::GetEntities() +{ + size_t count = 0; + BNSimilarityEntityId* entities = BNSimilaritySessionNodeGetEntities(m_object, &count); + std::vector result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(entities[i]); + BNFreeSimilarityEntityList(entities); + return result; +} + +bool SimilaritySessionNode::AddScheduledEntity(SimilarityEntityId id) +{ + return BNSimilaritySessionNodeAddScheduledEntity(m_object, id); +} + +bool SimilaritySessionNode::RemoveScheduledEntity(SimilarityEntityId id) +{ + return BNSimilaritySessionNodeRemoveScheduledEntity(m_object, id); +} + +std::vector SimilaritySessionNode::GetScheduledEntities() +{ + size_t count = 0; + BNSimilarityEntityId* entities = BNSimilaritySessionNodeGetScheduledEntities(m_object, &count); + std::vector result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(entities[i]); + BNFreeSimilarityEntityList(entities); + return result; +} + + +Ref SimilaritySessionNode::GetEntityFunction(const SimilarityEntityId id) +{ + BNFunction* function = BNSimilaritySessionNodeGetEntityFunction(m_object, id); + if (!function) + return nullptr; + return new Function(function); +} + +std::vector SimilaritySessionNode::GetResults(SimilarityEntityId entity) +{ + size_t count = 0; + BNSimilarityResultId* results = BNSimilaritySessionNodeGetResults(m_object, entity, &count); + std::vector out; + out.reserve(count); + for (size_t i = 0; i < count; i++) + out.push_back(results[i]); + BNFreeSimilarityResultIdList(results); + return out; +} + +std::optional SimilaritySessionNode::GetResult(SimilarityResultId resultId) +{ + BNSimilarityResult result; + if (!BNSimilaritySessionNodeGetResult(m_object, resultId, &result)) + return std::nullopt; + return SimilarityResult(result); +} + +bool SimilaritySessionNode::SetResolvedResult(SimilarityEntityId entity, SimilarityResultId result) +{ + return BNSimilaritySessionNodeSetResolvedResult(m_object, entity, result); +} + +std::optional SimilaritySessionNode::GetResolvedResult(SimilarityEntityId entity) +{ + BNSimilarityResultId result; + if (!BNSimilaritySessionNodeGetResolvedResult(m_object, entity, &result)) + return std::nullopt; + return result; +} + +bool SimilaritySessionNode::ClearResolvedResult(SimilarityEntityId entity) +{ + return BNSimilaritySessionNodeClearResolvedResult(m_object, entity); +} + + +std::vector SimilaritySessionNode::GetIncomingEdges() +{ + size_t count; + BNSimilaritySessionNodeId* edges = BNSimilaritySessionNodeGetIncomingEdges(m_object, &count); + std::vector result; + result.reserve(count); + for (size_t i = 0; i < count; ++i) + result.push_back(edges[i]); + BNFreeSimilaritySessionNodeEdgeList(edges); + return result; +} + + +std::vector SimilaritySessionNode::GetOutgoingEdges() +{ + size_t count; + BNSimilaritySessionNodeId* edges = BNSimilaritySessionNodeGetOutgoingEdges(m_object, &count); + std::vector result; + result.reserve(count); + for (size_t i = 0; i < count; ++i) + result.push_back(edges[i]); + BNFreeSimilaritySessionNodeEdgeList(edges); + return result; +} + + +std::vector> SimilaritySessionNode::GetIncomingNodes() +{ + size_t count = 0; + BNSimilaritySessionNode** nodes = BNSimilaritySessionNodeGetIncomingNodes(m_object, &count); + std::vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(nodes[i]))); + BNFreeSimilaritySessionNodeList(nodes, count); + return result; +} + + +std::vector> SimilaritySessionNode::GetOutgoingNodes() +{ + size_t count = 0; + BNSimilaritySessionNode** nodes = BNSimilaritySessionNodeGetOutgoingNodes(m_object, &count); + std::vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(nodes[i]))); + BNFreeSimilaritySessionNodeList(nodes, count); + return result; +} + + +SimilaritySessionGraph::SimilaritySessionGraph(BNSimilaritySessionGraph* graph) +{ + m_object = graph; +} + + +void SimilaritySessionGraph::AddNode(Ref node) +{ + BNSimilaritySessionGraphAddNode(m_object, node->GetObject()); +} + + +void SimilaritySessionGraph::RemoveNode(SimilaritySessionNode& node) +{ + BNSimilaritySessionGraphRemoveNode(m_object, node.GetObject()); +} + + +Ref SimilaritySessionGraph::GetNode(const SimilaritySessionNodeId id) +{ + BNSimilaritySessionNode* node = BNSimilaritySessionGraphGetNode(m_object, id); + if (!node) + return nullptr; + return new SimilaritySessionNode(node); +} + + +std::vector> SimilaritySessionGraph::GetNodes() +{ + size_t count; + BNSimilaritySessionNode** nodes = BNSimilaritySessionGraphGetNodes(m_object, &count); + std::vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(nodes[i]))); + BNFreeSimilaritySessionNodeList(nodes, count); + return result; +} + + +bool SimilaritySessionGraph::IsValidEdge(SimilaritySessionNode& from, SimilaritySessionNode& to) +{ + return BNSimilaritySessionGraphIsValidEdge(m_object, from.GetObject(), to.GetObject()); +} + + +bool SimilaritySessionGraph::AddEdge(SimilaritySessionNode& from, SimilaritySessionNode& to) +{ + return BNSimilaritySessionGraphAddEdge(m_object, from.GetObject(), to.GetObject()); +} + + +bool SimilaritySessionGraph::RemoveEdge(SimilaritySessionNode& from, SimilaritySessionNode& to) +{ + return BNSimilaritySessionGraphRemoveEdge(m_object, from.GetObject(), to.GetObject()); +} + + +void SimilaritySessionGraph::AddReceiver(Ref receiver) +{ + BNSimilaritySessionGraphAddReceiver(m_object, receiver->GetObject()); +} + + +void SimilaritySessionGraph::RemoveReceiver(SimilaritySessionGraphReceiver& receiver) +{ + BNSimilaritySessionGraphRemoveReceiver(m_object, receiver.GetObject()); +} + + +std::vector> SimilaritySessionGraph::GetReceivers() +{ + size_t count = 0; + BNSimilaritySessionGraphReceiver** receivers = BNSimilaritySessionGraphGetReceivers(m_object, &count); + std::vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + result.push_back( + new CoreSimilaritySessionGraphReceiver(BNNewSimilaritySessionGraphReceiverReference(receivers[i]))); + } + BNFreeSimilaritySessionGraphReceiverList(receivers, count); + return result; +} + + +std::vector>> SimilaritySessionGraph::GetSchedule() +{ + size_t* nodeCounts; + size_t levelCount; + BNSimilaritySessionNode*** schedule = BNSimilaritySessionGraphGetSchedule(m_object, &nodeCounts, &levelCount); + + std::vector>> result(levelCount); + for (size_t i = 0; i < levelCount; i++) + { + result[i].reserve(nodeCounts[i]); + for (size_t j = 0; j < nodeCounts[i]; j++) + result[i].push_back(new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(schedule[i][j]))); + } + BNFreeSimilaritySessionNodeSchedule(schedule, nodeCounts, levelCount); + return result; +} + + +SimilaritySessionGraphReceiver::SimilaritySessionGraphReceiver(BNSimilaritySessionGraphReceiver* receiver) +{ + m_object = receiver; +} + + +SimilaritySessionGraphReceiver::SimilaritySessionGraphReceiver() +{ + BNCustomSimilaritySessionGraphReceiver cb {}; + cb.context = this; + cb.onGraphChanged = OnGraphChangedCallback; + cb.free = FreeContextCallback; + + AddRefForRegistration(); + m_object = BNCreateCustomSimilaritySessionGraphReceiver(&cb); +} + + +CoreSimilaritySessionGraphReceiver::CoreSimilaritySessionGraphReceiver(BNSimilaritySessionGraphReceiver* receiver) : + SimilaritySessionGraphReceiver(receiver) +{} + + +void CoreSimilaritySessionGraphReceiver::NotifyGraphChanged() +{ + BNSimilaritySessionGraphReceiverNotifyGraphChanged(m_object); +} + + +SimilaritySession::SimilaritySession(BNSimilaritySession* session) +{ + m_object = session; +} + + +SimilaritySession::SimilaritySession() +{ + m_object = BNCreateSimilaritySession(); +} + +SimilaritySessionId SimilaritySession::GetId() const +{ + return BNSimilaritySessionGetId(m_object); +} + + +void SimilaritySession::AddProvider(Ref provider) +{ + BNSimilaritySessionAddProvider(m_object, provider->GetObject()); +} + + +void SimilaritySession::RemoveProvider(SimilarityProvider& provider) +{ + BNSimilaritySessionRemoveProvider(m_object, provider.GetObject()); +} + +bool SimilaritySession::UpdateProviderSettings(SimilarityProvider& provider, Settings& settings) +{ + return BNSimilaritySessionUpdateProviderSettings(m_object, provider.GetObject(), settings.GetObject()); +} + + +Ref SimilaritySession::GetProvider(SimilarityProviderId id) +{ + BNSimilarityProvider* provider = BNSimilaritySessionGetProvider(m_object, id); + if (!provider) + return nullptr; + return new CoreSimilarityProvider(provider); +} + + +std::vector> SimilaritySession::GetProviders() +{ + size_t count; + BNSimilarityProvider** providers = BNSimilaritySessionGetProviders(m_object, &count); + std::vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new CoreSimilarityProvider(BNNewSimilarityProviderReference(providers[i]))); + BNFreeSimilarityProviderList(providers, count); + return result; +} + + +bool SimilaritySession::AddResolver(Ref resolver) +{ + return BNSimilaritySessionAddResolver(m_object, resolver->GetObject()); +} + + +bool SimilaritySession::RemoveResolver(SimilaritySessionResolver& resolver) +{ + return BNSimilaritySessionRemoveResolver(m_object, resolver.GetObject()); +} + +bool SimilaritySession::UpdateResolverSettings(SimilaritySessionResolver& resolver, Settings& settings) +{ + return BNSimilaritySessionUpdateResolverSettings(m_object, resolver.GetObject(), settings.GetObject()); +} + + +Ref SimilaritySession::GetResolver(SimilaritySessionResolverId id) +{ + BNSimilaritySessionResolver* resolver = BNSimilaritySessionGetResolver(m_object, id); + if (!resolver) + return nullptr; + return new CoreSimilaritySessionResolver(resolver); +} + + +std::vector> SimilaritySession::GetResolvers() +{ + size_t count; + BNSimilaritySessionResolver** resolvers = BNSimilaritySessionGetResolvers(m_object, &count); + std::vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new CoreSimilaritySessionResolver(BNNewSimilaritySessionResolverReference(resolvers[i]))); + BNFreeSimilaritySessionResolverList(resolvers, count); + return result; +} + + +Ref SimilaritySession::GetGraph() +{ + return new SimilaritySessionGraph(BNSimilaritySessionGetGraph(m_object)); +} + + +void SimilaritySession::AddReceiver(Ref receiver) +{ + BNSimilaritySessionAddReceiver(m_object, receiver->GetObject()); +} + + +void SimilaritySession::RemoveReceiver(SimilaritySessionReceiver& receiver) +{ + BNSimilaritySessionRemoveReceiver(m_object, receiver.GetObject()); +} + + +std::vector> SimilaritySession::GetReceivers() +{ + size_t count; + BNSimilaritySessionReceiver** receivers = BNSimilaritySessionGetReceivers(m_object, &count); + std::vector> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new CoreSimilaritySessionReceiver(BNNewSimilaritySessionReceiverReference(receivers[i]))); + BNFreeSimilaritySessionReceiverList(receivers, count); + return result; +} + + +Ref SimilaritySession::Run() +{ + return new SimilaritySessionCompletion(BNSimilaritySessionRun(m_object)); +} + + +SimilaritySessionReceiver::SimilaritySessionReceiver(BNSimilaritySessionReceiver* receiver) +{ + m_object = receiver; +} + + +SimilaritySessionReceiver::SimilaritySessionReceiver() +{ + BNCustomSimilaritySessionReceiver cb {}; + cb.context = this; + cb.onStarted = OnStartedCallback; + cb.onUpdated = OnUpdatedCallback; + cb.free = FreeContextCallback; + + AddRefForRegistration(); + m_object = BNCreateCustomSimilaritySessionReceiver(&cb); +} + + +CoreSimilaritySessionReceiver::CoreSimilaritySessionReceiver(BNSimilaritySessionReceiver* receiver) : + SimilaritySessionReceiver(receiver) +{} + + +void CoreSimilaritySessionReceiver::NotifyStart(SimilaritySessionCompletion& completion) +{ + BNSimilaritySessionReceiverNotifyStart(m_object, completion.GetObject()); +} + + +void CoreSimilaritySessionReceiver::NotifyBatch( + SimilaritySessionNode& node, SimilarityProvider& provider, const std::vector& entities) +{ + std::vector coreEntities; + coreEntities.reserve(entities.size()); + for (const auto& entity : entities) + coreEntities.push_back(entity); + BNSimilaritySessionReceiverNotifyBatch( + m_object, node.GetObject(), provider.GetObject(), coreEntities.data(), coreEntities.size()); +} + + +SimilaritySessionCompletion::SimilaritySessionCompletion(BNSimilaritySessionCompletion* completion) +{ + m_object = completion; +} + + +SimilaritySessionCompletion::SimilaritySessionCompletion() +{ + m_object = BNCreateSimilaritySessionCompletion(); +} + +bool SimilaritySessionCompletion::IsFinished() const +{ + return BNSimilaritySessionCompletionIsFinished(m_object); +} + +void SimilaritySessionCompletion::RequestStop() +{ + BNSimilaritySessionCompletionRequestStop(m_object); +} + +bool SimilaritySessionCompletion::IsStopRequested() const +{ + return BNSimilaritySessionCompletionIsStopRequested(m_object); +} + +double SimilaritySessionCompletion::GetProgress(const SimilaritySessionCompletionQuery& query) const +{ + const BNSimilaritySessionCompletionQuery rawQuery = query.ToRaw(); + return BNSimilaritySessionCompletionGetProgress(m_object, &rawQuery); +} + +void SimilaritySessionCompletion::SetProgress(const SimilaritySessionCompletionQuery& query, double progress) +{ + const BNSimilaritySessionCompletionQuery rawQuery = query.ToRaw(); + BNSimilaritySessionCompletionSetProgress(m_object, &rawQuery, progress); +} + +std::chrono::milliseconds SimilaritySessionCompletion::GetTiming(const SimilaritySessionCompletionQuery& query) const +{ + const BNSimilaritySessionCompletionQuery rawQuery = query.ToRaw(); + return std::chrono::milliseconds(BNSimilaritySessionCompletionGetTiming(m_object, &rawQuery)); +} + + +BNSimilarityProvider* SimilarityProviderType::CreateCallback(void* ctxt, BNSettings* settings) +{ + return RunSimilarityCallback< + BNSimilarityProvider*>("Unhandled exception creating similarity provider", nullptr, [&]() { + SimilarityProviderType* type = (SimilarityProviderType*)ctxt; + Ref settingsObj = new Settings(BNNewSettingsReference(settings)); + Ref result = type->Create(*settingsObj); + if (!result) + return static_cast(nullptr); + return BNNewSimilarityProviderReference(result->GetObject()); + }); +} + + +BNSettings* SimilarityProviderType::GetDefaultSettingsCallback(void* ctxt) +{ + return RunSimilarityCallback< + BNSettings*>("Unhandled exception getting similarity provider settings", nullptr, [&]() { + SimilarityProviderType* type = (SimilarityProviderType*)ctxt; + Ref result = type->GetDefaultSettings(); + if (!result) + return static_cast(nullptr); + return BNNewSettingsReference(result->m_object); + }); +} + + +bool SimilarityProvider::VisitNodeCallback(void* ctxt, BNSimilaritySessionNode* node, + BNSimilarityProviderResults* results, BNSimilaritySessionCompletion* completion) +{ + try + { + CallbackRef provider(ctxt); + Ref nodeObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(node)); + SimilarityProviderResults resultsObj(results); + Ref completionObj = + new SimilaritySessionCompletion(BNNewSimilaritySessionCompletionReference(completion)); + return provider->VisitNode(*nodeObj, resultsObj, *completionObj); + } + catch (const std::exception& exception) + { + LogErrorForException(exception, "Unhandled exception in similarity provider node visit"); + return false; + } + catch (...) + { + LogError("Unhandled exception in similarity provider node visit"); + return false; + } +} + + +bool SimilarityProvider::VisitNodeEdgeCallback(void* ctxt, BNSimilaritySessionNode* from, BNSimilaritySessionNode* to, + BNSimilarityProviderResults* results, BNSimilaritySessionCompletion* completion) +{ + try + { + CallbackRef provider(ctxt); + Ref fromObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(from)); + Ref toObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(to)); + SimilarityProviderResults resultsObj(results); + Ref completionObj = + new SimilaritySessionCompletion(BNNewSimilaritySessionCompletionReference(completion)); + return provider->VisitNodeEdge(*fromObj, *toObj, resultsObj, *completionObj); + } + catch (const std::exception& exception) + { + LogErrorForException(exception, "Unhandled exception in similarity provider edge visit"); + return false; + } + catch (...) + { + LogError("Unhandled exception in similarity provider edge visit"); + return false; + } +} + + +char* SimilarityProvider::GetNameCallback( + void* ctxt, BNSimilaritySessionNode* node, BNSimilarityEntityId entity, BNSimilarityResultId resultId) +{ + return RunSimilarityCallback("Unhandled exception getting similarity result name", nullptr, [&]() { + CallbackRef provider(ctxt); + Ref nodeObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(node)); + std::optional name = provider->GetName(*nodeObj, entity, resultId); + if (!name) + return static_cast(nullptr); + return BNAllocString(name->c_str()); + }); +} + + +BNSimilarityApplyStatus SimilarityProvider::ApplyCallback(void* ctxt, BNSimilaritySessionNode* node, + BNSimilarityEntityId entity, BNSimilarityResultId resultId) +{ + return RunSimilarityCallback("Unhandled exception applying similarity result", SimilarityApplyFailed, [&]() { + CallbackRef provider(ctxt); + Ref nodeObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(node)); + return provider->Apply(*nodeObj, entity, resultId); + }); +} + + +void SimilarityProvider::RenderCallback(void* ctxt, BNSimilaritySessionNode* node, + BNSimilarityEntityId entity, BNSimilarityRenderContext* context, BNSimilarityResultId resultId) +{ + RunSimilarityCallback("Unhandled exception rendering similarity result", [&]() { + CallbackRef provider(ctxt); + Ref nodeObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(node)); + Ref contextObj = + new SimilarityRenderContext(BNNewSimilarityRenderContextReference(context)); + provider->Render(*nodeObj, entity, *contextObj, resultId); + }); +} + + +void SimilarityProvider::FreeContextCallback(void* ctxt) +{ + SimilarityProvider* provider = (SimilarityProvider*)ctxt; + provider->ReleaseForRegistration(); +} + + +void SimilaritySessionResolver::ResolveForNodeCallback(void* ctxt, BNSimilaritySession* session, + BNSimilaritySessionNode* node, const BNSimilarityEntityId* entities, size_t entityCount, + BNSimilaritySessionCompletion* completion, BNSimilaritySessionResolverId resolverId) +{ + RunSimilarityCallback("Unhandled exception resolving similarity results", [&]() { + (void)resolverId; + CallbackRef resolver(ctxt); + Ref sessionObj = new SimilaritySession(BNNewSimilaritySessionReference(session)); + Ref nodeObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(node)); + Ref completionObj = + new SimilaritySessionCompletion(BNNewSimilaritySessionCompletionReference(completion)); + std::vector entityList; + entityList.reserve(entityCount); + for (size_t i = 0; i < entityCount; i++) + entityList.push_back(entities[i]); + resolver->ResolveForNode(*sessionObj, *nodeObj, entityList, *completionObj); + }); +} + + +void SimilaritySessionResolver::PrepareForNodeCallback(void* ctxt, BNSimilaritySession* session, + BNSimilaritySessionNode* node, BNSimilaritySessionCompletion* completion, BNSimilaritySessionResolverId resolverId) +{ + RunSimilarityCallback("Unhandled exception preparing similarity resolver", [&]() { + (void)resolverId; + CallbackRef resolver(ctxt); + Ref sessionObj = new SimilaritySession(BNNewSimilaritySessionReference(session)); + Ref nodeObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(node)); + Ref completionObj = + new SimilaritySessionCompletion(BNNewSimilaritySessionCompletionReference(completion)); + resolver->PrepareForNode(*sessionObj, *nodeObj, *completionObj); + }); +} + + +void SimilaritySessionResolver::FreeContextCallback(void* ctxt) +{ + SimilaritySessionResolver* resolver = (SimilaritySessionResolver*)ctxt; + resolver->ReleaseForRegistration(); +} + + +void SimilaritySessionReceiver::OnStartedCallback(void* ctxt, BNSimilaritySessionCompletion* completion) +{ + RunSimilarityCallback("Unhandled exception starting similarity session receiver", [&]() { + CallbackRef receiver(ctxt); + Ref completionObj = + new SimilaritySessionCompletion(BNNewSimilaritySessionCompletionReference(completion)); + receiver->NotifyStart(*completionObj); + }); +} + + +void SimilaritySessionReceiver::OnUpdatedCallback(void* ctxt, BNSimilaritySessionNode* node, + BNSimilarityProvider* provider, const BNSimilarityEntityId* entities, size_t count) +{ + RunSimilarityCallback("Unhandled exception updating similarity session receiver", [&]() { + CallbackRef receiver(ctxt); + Ref nodeObj = new SimilaritySessionNode(BNNewSimilaritySessionNodeReference(node)); + Ref providerObj = new CoreSimilarityProvider(BNNewSimilarityProviderReference(provider)); + + std::vector entityList; + entityList.reserve(count); + for (size_t i = 0; i < count; i++) + entityList.push_back(entities[i]); + + receiver->NotifyBatch(*nodeObj, *providerObj, entityList); + }); +} + + +void SimilaritySessionReceiver::FreeContextCallback(void* ctxt) +{ + SimilaritySessionReceiver* receiver = (SimilaritySessionReceiver*)ctxt; + receiver->ReleaseForRegistration(); +} + + +void SimilaritySessionGraphReceiver::OnGraphChangedCallback(void* ctxt) +{ + RunSimilarityCallback("Unhandled exception updating similarity session graph receiver", [&]() { + CallbackRef receiver(ctxt); + receiver->NotifyGraphChanged(); + }); +} + + +void SimilaritySessionGraphReceiver::FreeContextCallback(void* ctxt) +{ + SimilaritySessionGraphReceiver* receiver = (SimilaritySessionGraphReceiver*)ctxt; + receiver->ReleaseForRegistration(); +} diff --git a/vendor/bindiff b/vendor/bindiff new file mode 160000 index 0000000000..56b5f82eb6 --- /dev/null +++ b/vendor/bindiff @@ -0,0 +1 @@ +Subproject commit 56b5f82eb699fa2fb72e29479da15df739ab2ef9 diff --git a/vendor/binexport b/vendor/binexport new file mode 160000 index 0000000000..2707c2d1ec --- /dev/null +++ b/vendor/binexport @@ -0,0 +1 @@ +Subproject commit 2707c2d1ecc8a65a9e75b662d124c40884a2af0e