diff options
| author | kat <katherine@vector35.com> | 2022-09-22 09:25:01 -0400 |
|---|---|---|
| committer | kat <katherine@vector35.com> | 2022-09-22 09:25:01 -0400 |
| commit | e95b220ef18c9dd9e1aecab32b735ff47237f9c7 (patch) | |
| tree | e302ace94bc5c8707edbc037c1fea55cfabc73cd | |
| parent | 07be3689f173e7a55af5e795c10377e0afdd594f (diff) | |
Add the Components API
| -rw-r--r-- | binaryninjaapi.h | 311 | ||||
| -rw-r--r-- | binaryninjacore.h | 52 | ||||
| -rw-r--r-- | binaryview.cpp | 163 | ||||
| -rw-r--r-- | component.cpp | 163 | ||||
| -rw-r--r-- | function.cpp | 18 | ||||
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/binaryview.py | 203 | ||||
| -rw-r--r-- | python/component.py | 266 | ||||
| -rw-r--r-- | python/function.py | 10 |
9 files changed, 1186 insertions, 1 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 6f0ca20a..752b9c52 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1586,6 +1586,7 @@ namespace BinaryNinja { struct TagReference; class Section; class Segment; + class Component; class BinaryDataNotification { @@ -1624,6 +1625,14 @@ namespace BinaryNinja { static void SectionAddedCallback(void* ctx, BNBinaryView* data, BNSection* section); static void SectionUpdatedCallback(void* ctx, BNBinaryView* data, BNSection* section); static void SectionRemovedCallback(void* ctx, BNBinaryView* data, BNSection* section); + static void ComponentNameUpdatedCallback(void* ctxt, BNBinaryView* data, char* previousName, BNComponent* component); + static void ComponentAddedCallback(void* ctxt, BNBinaryView* data, BNComponent* component); + static void ComponentRemovedCallback( + void* ctxt, BNBinaryView* data, BNComponent* formerParent, BNComponent* component); + static void ComponentMovedCallback( + void* ctxt, BNBinaryView* data, BNComponent* formerParent, BNComponent* newParent, BNComponent* component); + static void ComponentFunctionAddedCallback(void* ctxt, BNBinaryView* data, BNComponent* component, BNFunction* function); + static void ComponentFunctionRemovedCallback(void* ctxt, BNBinaryView* data, BNComponent* component, BNFunction* function); public: @@ -1793,6 +1802,91 @@ namespace BinaryNinja { (void)data; (void)section; } + + /*! This notification is posted after the display name for a component is updated. + + \param data BinaryView the Component is contained in + \param previousName Previous name of the component + \param component The component which was modified. + */ + virtual void OnComponentNameUpdated(BinaryView* data, std::string& previousName, Component* component) + { + (void)data; + (void)previousName; + (void)component; + } + + /*! This notification is posted after a Component is added to the tree. + + \param data BinaryView the Component was added to + \param component Component which was added. + */ + virtual void OnComponentAdded(BinaryView* data, Component* component) + { + (void)data; + (void)component; + } + + /*! This notification is posted after a Component is removed from the tree. + + \param data BinaryView the Component was removed from + \param formerParent Former parent of the Component + \param component + \parblock + The removed and now "dead" Component object. + + This "dead" Component can no longer be moved to other components or have components added to it. It + should not be used after this point for storing any objects, and will be destroyed once no more references + are held to it. + \endparblock + */ + virtual void OnComponentRemoved(BinaryView* data, Component* formerParent, Component* component) + { + (void)data; + (void)formerParent; + (void)component; + } + + /*! This notification is posted whenever a component is moved from one component to another. + + \param data BinaryView the Component was removed from + \param formerParent Former parent of the Component + \param newParent New parent which the Component was moved to + \param component The component that was moved. + */ + virtual void OnComponentMoved(BinaryView* data, Component* formerParent, Component* newParent, Component* component) + { + (void)data; + (void)formerParent; + (void)newParent; + (void)component; + } + + /*! This notification is posted whenever a Function is added to a Component + + \param data BinaryView containing the Component and Function + \param component Component the Function was added to + \param function The Function which was added + */ + virtual void OnComponentFunctionAdded(BinaryView* data, Component* component, Function* function) + { + (void)data; + (void)component; + (void)function; + } + + /*! This notification is posted whenever a Function is removed from a Component + + \param data BinaryView containing the Component and Function + \param component Component the Function was removed from + \param function The Function which was removed + */ + virtual void OnComponentFunctionRemoved(BinaryView* data, Component* component, Function* function) + { + (void)data; + (void)component; + (void)function; + } }; class FileAccessor @@ -2367,6 +2461,7 @@ namespace BinaryNinja { class Structure; class NamedTypeReference; struct TypeParserResult; + class Component; class QueryMetadataException : public std::exception { @@ -3643,6 +3738,96 @@ namespace BinaryNinja { Ref<Tag> CreateAutoDataTag(uint64_t addr, Ref<TagType> tagType, const std::string& data, bool unique = false); Ref<Tag> CreateUserDataTag(uint64_t addr, Ref<TagType> tagType, const std::string& data, bool unique = false); + /*! Lookup a component by its GUID + + \param guid GUID of the component to look up + \return The component with that GUID + */ + std::optional<Ref<Component>> GetComponentByGuid(std::string guid); + + /*! Lookup a component by its pathname + + \note This is a convenience method, and for performance-sensitive lookups, GetComponentByGuid is very + highly recommended. + + \see GetComponentByGuid, Component::GetGuid + + All lookups are absolute from the root component, and are case-sensitive. Pathnames are delimited with "/" + + Lookups are done using the display name of the component, which is liable to change when it or its siblings + are moved around. + + \see Component::GetDisplayName + + \param path Path of the desired component + \return The component at that path + */ + std::optional<Ref<Component>> GetComponentByPath(std::string path); + + /*! Get the root component for the BinaryView (read-only) + + This Component cannot be removed, and houses all unparented Components. + + \return The Root Component + */ + Ref<Component> GetRootComponent(); + + /*! Create a component + + This component will be added to the root component and initialized with the name "Component" + + \return The created Component + */ + Ref<Component> CreateComponent(); + + /*! Create a component as a subcomponent of the component with a given Guid + + This component will be initialized with the name "Component" + + \param parentGUID Guid of the component this component will be added to + \return The created Component + */ + Ref<Component> CreateComponent(std::string parentGUID); + + /*! Create a component as a subcomponent of a given Component + + This component will be initialized with the name "Component" + + \param parent Parent Component + \return The created Component + */ + Ref<Component> CreateComponent(Ref<Component> parent); + + /*! Create a component with a given name and optional parent + + \param name Name to initialize the component with + \param parentGUID Optional Guid of the component this component will be added to + \return The created Component + */ + Ref<Component> CreateComponentWithName(std::string name, std::string parentGUID = {}); + + /*! Create a component with a given name and parent + + \param name Name to initialize the component with + \param parentGUID Guid of the component this component will be added to + \return The created Component + */ + Ref<Component> CreateComponentWithName(std::string name, Ref<Component> parent); + + /*! Remove a component from the tree entirely. This will also by nature remove all subcomponents. + + \param component Component to remove + \return Whether removal was successful + */ + bool RemoveComponent(Ref<Component> component); + + /*! Remove a component from the tree entirely. This will also by nature remove all subcomponents. + + \param guid Guid of the Component to remove + \return Whether removal was successful + */ + bool RemoveComponent(std::string guid); + /*! Check whether the given architecture supports assembling instructions \param arch Architecture to check @@ -7340,6 +7525,7 @@ namespace BinaryNinja { }; class FlowGraph; + class Component; struct SSAVariable; class Function : public CoreRefCountObject<BNFunction, BNNewFunctionReference, BNFreeFunction> @@ -7409,6 +7595,7 @@ namespace BinaryNinja { \return Whether this function needs update */ bool NeedsUpdate() const; + std::vector<Ref<Component>> GetParentComponents() const; /*! Get a list of Basic Blocks for this function @@ -11857,4 +12044,128 @@ namespace BinaryNinja { virtual bool StoreData(const std::string& key, const std::string& data) override; virtual bool DeleteData(const std::string& key) override; }; + + /*! Components are objects that can contain Functions and other Components. + + \note Components should not be instantiated directly. Instead use BinaryView::CreateComponent() + + They can be queried for information about the functions contained within them. + + Components have a Guid, which persistent across saves and loads of the database, and should be + used for retrieving components when such is required and a reference to the Component cannot be held. + + */ + class Component : public CoreRefCountObject<BNComponent, BNNewComponentReference, BNFreeComponent> + { + public: + Component(BNComponent* type); + + /*! Get the unique identifier for this component. + + \return Component GUID + */ + std::string GetGuid(); + + bool operator==(const Component& other) const; + bool operator!=(const Component& other) const; + + Ref<BinaryView> GetView(); + + /*! The displayed name for the component + + This can differ from the GetOriginalName() value if the parent + component also contains other components with the same name. + + Subsequent duplicates will return the original name with " (1)", " (2)" and so on appended. + + This name can change whenever a different duplicate is removed. + + \note For looking up Components, utilizing Guid is highly recommended, as it will *always* map to this component, + and as Guid lookups are faster by nature. + + \return Component name + */ + std::string GetDisplayName(); + + /*! The original name for the component + + This may differ from Component::GetName() whenever the parent contains Components with the same original name. + + This function will always return the value originally set for this Component. + + \return Component name + */ + std::string GetName(); + + /*! Set the name for the component + + \see GetName(), GetOriginalName() + + \param name New component name. + */ + void SetName(const std::string &name); + + /*! Get the parent component. If it's a top level component, it will return the "root" Component. + + \return Parent Component + */ + Ref<Component> GetParent(); + + /*! Add a function to this component + + \param func Function to add. + \return True if the function was successfully added. + */ + bool AddFunction(Ref<Function> func); + + /*! Move a component to this component. + + \param component Component to add. + \return True if the component was successfully added. + */ + bool AddComponent(Ref<Component> component); + + /*! Remove a Component from this Component, moving it to the root component. + + This will not remove a component from the tree entirely. + + \see BinaryView::GetRootComponent(), BinaryView::RemoveComponent() + + \param component Component to remove + \return True if the component was successfully removed + */ + bool RemoveComponent(Ref<Component> component); + + /*! Remove a function + + \param func Function to remove + \return True if the function was successfully removed. + */ + bool RemoveFunction(Ref<Function> func); + + /*! Get a list of types referenced by the functions in this Component. + + \return vector of Type objects + */ + std::vector<Ref<Type>> GetReferencedTypes(); + + /*! Get a list of components contained by this component. + + \return vector of Component objects + */ + std::vector<Ref<Component>> GetContainedComponents(); + + /*! Get a list of functions contained within this Component. + + \return vector of Function objects + */ + std::vector<Ref<Function>> GetContainedFunctions(); + + /*! Get a list of DataVariables referenced by the functions in this Component. + + \return vector of DataVariable objects + */ + std::vector<DataVariable> GetReferencedDataVariables(); + }; + } // namespace BinaryNinja diff --git a/binaryninjacore.h b/binaryninjacore.h index 212c8b62..12c1b277 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -220,6 +220,7 @@ extern "C" struct BNRepository; struct BNRepoPlugin; struct BNRepositoryManager; + struct BNComponent; struct BNSettings; struct BNMetadata; struct BNReportCollection; @@ -1379,6 +1380,12 @@ extern "C" void (*sectionAdded)(void* ctxt, BNBinaryView* view, BNSection* section); void (*sectionUpdated)(void* ctxt, BNBinaryView* view, BNSection* section); void (*sectionRemoved)(void* ctxt, BNBinaryView* view, BNSection* section); + void (*componentNameUpdated)(void* ctxt, BNBinaryView* view, char* previousName, BNComponent* component); + void (*componentAdded)(void*ctxt, BNBinaryView* view, BNComponent* component); + void (*componentMoved)(void*ctxt, BNBinaryView* view, BNComponent* formerParent, BNComponent* newParent, BNComponent* component); + void (*componentRemoved)(void*ctxt, BNBinaryView* view, BNComponent* formerParent, BNComponent* component); + void (*componentFunctionAdded)(void*ctxt, BNBinaryView* view, BNComponent* component, BNFunction* function); + void (*componentFunctionRemoved)(void*ctxt, BNBinaryView* view, BNComponent* component, BNFunction* function); }; struct BNFileAccessor @@ -3324,6 +3331,16 @@ extern "C" BINARYNINJACOREAPI BNSegment* BNGetSegmentAt(BNBinaryView* view, uint64_t addr); BINARYNINJACOREAPI bool BNGetAddressForDataOffset(BNBinaryView* view, uint64_t offset, uint64_t* addr); + BINARYNINJACOREAPI BNComponent* BNGetComponentByGuid(BNBinaryView* view, const char *guid); + BINARYNINJACOREAPI BNComponent* BNGetRootComponent(BNBinaryView* view); + BINARYNINJACOREAPI BNComponent* BNCreateComponent(BNBinaryView* view); + BINARYNINJACOREAPI BNComponent* BNCreateComponentWithParent(BNBinaryView* view, const char* parentGUID); + BINARYNINJACOREAPI BNComponent* BNCreateComponentWithName(BNBinaryView* view, const char *name); + BINARYNINJACOREAPI BNComponent* BNCreateComponentWithParentAndName(BNBinaryView* view, const char* parentGUID, const char *name); + BINARYNINJACOREAPI BNComponent* BNGetComponentByPath(BNBinaryView* view, const char* path); + BINARYNINJACOREAPI bool BNRemoveComponent(BNBinaryView* view, BNComponent* component); + BINARYNINJACOREAPI bool BNRemoveComponentByGuid(BNBinaryView* view, const char *guid); + BINARYNINJACOREAPI void BNAddAutoSection(BNBinaryView* view, const char* name, uint64_t start, uint64_t length, BNSectionSemantics semantics, const char* type, uint64_t align, uint64_t entrySize, const char* linkedSection, const char* infoSection, uint64_t infoData); @@ -3703,6 +3720,7 @@ extern "C" BINARYNINJACOREAPI uint64_t BNGetFunctionHighestAddress(BNFunction* func); BINARYNINJACOREAPI uint64_t BNGetFunctionLowestAddress(BNFunction* func); BINARYNINJACOREAPI BNAddressRange* BNGetFunctionAddressRanges(BNFunction* func, size_t* count); + BINARYNINJACOREAPI BNComponent** BNGetFunctionParentComponents(BNFunction *func, size_t* count); BINARYNINJACOREAPI BNLowLevelILFunction* BNGetFunctionLowLevelIL(BNFunction* func); BINARYNINJACOREAPI BNLowLevelILFunction* BNGetFunctionLowLevelILIfAvailable(BNFunction* func); @@ -5933,6 +5951,40 @@ extern "C" BINARYNINJACOREAPI BNRepository* BNRepositoryManagerGetDefaultRepository(BNRepositoryManager* r); + // Components + + BINARYNINJACOREAPI BNComponent* BNNewComponentReference(BNComponent *component); + BINARYNINJACOREAPI void BNFreeComponent(BNComponent *component); + + BINARYNINJACOREAPI BNFunction** BNComponentGetContainedFunctions(BNComponent *component, size_t *count); + BINARYNINJACOREAPI BNComponent** BNComponentGetContainedComponents(BNComponent *component, size_t *count); + + BINARYNINJACOREAPI BNDataVariable* BNComponentGetReferencedDataVariables(BNComponent *component, size_t *count); + BINARYNINJACOREAPI BNDataVariable* BNComponentGetReferencedDataVariablesRecursive(BNComponent *component, size_t *count); + BINARYNINJACOREAPI BNType** BNComponentGetReferencedTypes(BNComponent *component, size_t *count); + BINARYNINJACOREAPI BNType** BNComponentGetReferencedTypesRecursive(BNComponent *component, size_t *count); + + BINARYNINJACOREAPI void BNFreeComponents(BNComponent** components, size_t count); + BINARYNINJACOREAPI void BNComponentFreeReferencedTypes(BNType** types, size_t count); + + BINARYNINJACOREAPI BNComponent* BNComponentGetParent(BNComponent* component); + + BINARYNINJACOREAPI bool BNComponentContainsFunction(BNComponent* component, BNFunction *function); + BINARYNINJACOREAPI bool BNComponentContainsComponent(BNComponent *parent, BNComponent *component); + BINARYNINJACOREAPI bool BNComponentAddFunctionReference(BNComponent* component, BNFunction* function); + BINARYNINJACOREAPI bool BNComponentAddComponent(BNComponent* parent, BNComponent* component); + BINARYNINJACOREAPI bool BNComponentRemoveComponent(BNComponent* component); + BINARYNINJACOREAPI void BNComponentAddAllMembersFromComponent(BNComponent *component, BNComponent *fromComponent); + BINARYNINJACOREAPI bool BNComponentRemoveFunctionReference(BNComponent *component, BNFunction *function); + BINARYNINJACOREAPI void BNComponentRemoveAllFunctions(BNComponent *component); + BINARYNINJACOREAPI char *BNComponentGetGuid(BNComponent *component); + BINARYNINJACOREAPI bool BNComponentsEqual(BNComponent* a, BNComponent* b); + BINARYNINJACOREAPI bool BNComponentsNotEqual(BNComponent* a, BNComponent* b); + BINARYNINJACOREAPI char *BNComponentGetDisplayName(BNComponent* component); + BINARYNINJACOREAPI char *BNComponentGetOriginalName(BNComponent* component); + BINARYNINJACOREAPI void BNComponentSetName(BNComponent* component, const char* name); + BINARYNINJACOREAPI BNBinaryView* BNComponentGetView(BNComponent* component); + // LLVM Services APIs BINARYNINJACOREAPI void BNLlvmServicesInit(void); diff --git a/binaryview.cpp b/binaryview.cpp index db45cc88..fa00b4e9 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -299,6 +299,71 @@ void BinaryDataNotification::SectionRemovedCallback(void* ctxt, BNBinaryView* da notify->OnSectionRemoved(view, sectionObj); } +void BinaryDataNotification::ComponentNameUpdatedCallback(void* ctxt, BNBinaryView* data, char *previousName, BNComponent* bnComponent) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<Component> component = new Component(BNNewComponentReference(bnComponent)); + std::string prevName = previousName; + BNFreeString(previousName); + notify->OnComponentNameUpdated(view, prevName, component); +} + + +void BinaryDataNotification::ComponentAddedCallback(void* ctxt, BNBinaryView* data, BNComponent* bnComponent) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<Component> component = new Component(BNNewComponentReference(bnComponent)); + notify->OnComponentAdded(view, component); +} + + +void BinaryDataNotification::ComponentRemovedCallback(void* ctxt, BNBinaryView* data, BNComponent* bnFormerParent, + BNComponent* bnComponent) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<Component> formerParent = new Component(BNNewComponentReference(bnFormerParent)); + Ref<Component> component = new Component(BNNewComponentReference(bnComponent)); + notify->OnComponentRemoved(view, formerParent, component); +} + + +void BinaryDataNotification::ComponentMovedCallback(void* ctxt, BNBinaryView* data, BNComponent* bnFormerParent, + BNComponent* bnNewParent, BNComponent* bnComponent) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<Component> formerParent = new Component(BNNewComponentReference(bnFormerParent)); + Ref<Component> newParent = new Component(BNNewComponentReference(bnNewParent)); + Ref<Component> component = new Component(BNNewComponentReference(bnComponent)); + notify->OnComponentMoved(view, formerParent, newParent, component); +} + + +void BinaryDataNotification::ComponentFunctionAddedCallback(void* ctxt, BNBinaryView* data, BNComponent* bnComponent, + BNFunction* func) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<Component> component = new Component(BNNewComponentReference(bnComponent)); + Ref<Function> function = new Function(BNNewFunctionReference(func)); + notify->OnComponentFunctionAdded(view, component, function); +} + + +void BinaryDataNotification::ComponentFunctionRemovedCallback(void* ctxt, BNBinaryView* data, BNComponent* bnComponent, + BNFunction* func) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<Component> component = new Component(BNNewComponentReference(bnComponent)); + Ref<Function> function = new Function(BNNewFunctionReference(func)); + notify->OnComponentFunctionRemoved(view, component, function); +} + + BinaryDataNotification::BinaryDataNotification() { @@ -333,6 +398,12 @@ BinaryDataNotification::BinaryDataNotification() m_callbacks.sectionAdded = SectionAddedCallback; m_callbacks.sectionUpdated = SectionUpdatedCallback; m_callbacks.sectionRemoved = SectionRemovedCallback; + m_callbacks.componentNameUpdated = ComponentNameUpdatedCallback; + m_callbacks.componentAdded = ComponentAddedCallback; + m_callbacks.componentRemoved = ComponentRemovedCallback; + m_callbacks.componentMoved = ComponentMovedCallback; + m_callbacks.componentFunctionAdded = ComponentFunctionAddedCallback; + m_callbacks.componentFunctionRemoved = ComponentFunctionRemovedCallback; } @@ -2873,11 +2944,103 @@ Ref<Tag> BinaryView::CreateUserDataTag(uint64_t addr, Ref<TagType> tagType, cons return tag; } + +std::optional<Ref<Component>> BinaryView::GetComponentByGuid(std::string guid) +{ + BNComponent* bncomponent = BNGetComponentByGuid(m_object, guid.c_str()); + + if (bncomponent) + { + auto component = new Component(bncomponent); + return std::optional<Ref<Component>>{component}; + } + + return std::nullopt; +} + +Ref<Component> BinaryView::GetRootComponent() +{ + BNComponent* bncomponent = BNGetRootComponent(m_object); + + return new Component(bncomponent); +} + + +std::optional<Ref<Component>> BinaryView::GetComponentByPath(std::string path) +{ + BNComponent* component = BNGetComponentByPath(this->m_object, path.c_str()); + if (component) + return {new Component(component)}; + return std::nullopt; +} + + +Ref<Component> BinaryView::CreateComponent() +{ + auto bnComponent = BNCreateComponent(m_object); + + return new Component(bnComponent); +} + + +Ref<Component> BinaryView::CreateComponent(std::string parentGUID) +{ + BNComponent* bnComponent; + if (!parentGUID.empty()) + bnComponent = BNCreateComponentWithParent(m_object, parentGUID.c_str()); + else + bnComponent = BNCreateComponent(m_object); + + return new Component(bnComponent); +} + + +Ref<Component> BinaryView::CreateComponent(Ref<Component> parent) +{ + auto bnComponent = BNCreateComponentWithParent(m_object, parent->GetGuid().c_str()); + + return new Component(bnComponent); +} + + +Ref<Component> BinaryView::CreateComponentWithName(std::string name, std::string parentGUID) +{ + BNComponent* bnComponent; + if (!parentGUID.empty()) + bnComponent = BNCreateComponentWithParentAndName(m_object, parentGUID.c_str(), name.c_str()); + else + bnComponent = BNCreateComponentWithName(m_object, name.c_str()); + + return new Component(bnComponent); +} + + +Ref<Component> BinaryView::CreateComponentWithName(std::string name, Ref<Component> parent) +{ + auto bnComponent = BNCreateComponentWithParentAndName(m_object, parent->GetGuid().c_str(), name.c_str()); + + return new Component(bnComponent); +} + + +bool BinaryView::RemoveComponent(Ref<Component> component) +{ + return BNRemoveComponent(m_object, component->m_object); +} + + +bool BinaryView::RemoveComponent(std::string guid) +{ + return BNRemoveComponentByGuid(m_object, guid.c_str()); +} + + bool BinaryView::CanAssemble(Architecture* arch) { return BNCanAssemble(m_object, arch->GetObject()); } + bool BinaryView::IsNeverBranchPatchAvailable(Architecture* arch, uint64_t addr) { return BNIsNeverBranchPatchAvailable(m_object, arch->GetObject(), addr); diff --git a/component.cpp b/component.cpp new file mode 100644 index 00000000..88e7a20a --- /dev/null +++ b/component.cpp @@ -0,0 +1,163 @@ + +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + +bool Component::operator==(const Component& other) const +{ + return BNComponentsEqual(m_object, other.m_object); +} + + +bool Component::operator!=(const Component& other) const +{ + return BNComponentsNotEqual(m_object, other.m_object); +} + + + +Component::Component(BNComponent* component) +{ + m_object = component; +} + + +std::string Component::GetDisplayName() +{ + return BNComponentGetDisplayName(m_object); +} + + +std::string Component::GetName() +{ + return BNComponentGetOriginalName(m_object); +} + + +Ref<BinaryView> Component::GetView() +{ + return new BinaryView(BNComponentGetView(m_object)); +} + + +void Component::SetName(const std::string &name) +{ + BNComponentSetName(m_object, name.c_str()); +} + + +Ref<Component> Component::GetParent() +{ + return new Component(BNComponentGetParent(m_object)); +} + + +std::string Component::GetGuid() +{ + return string(BNComponentGetGuid(m_object)); +} + + +bool Component::AddFunction(Ref<Function> func) +{ + return BNComponentAddFunctionReference(m_object, func->GetObject()); +} + + +bool Component::AddComponent(Ref<Component> component) +{ + return BNComponentAddComponent(m_object, component->m_object); +} + + +bool Component::RemoveComponent(Ref<Component> component) +{ + return BNComponentRemoveComponent(component->m_object); +} + + +bool Component::RemoveFunction(Ref<Function> func) +{ + return BNComponentRemoveFunctionReference(m_object, func->GetObject()); +} + + +std::vector<Ref<Component>> Component::GetContainedComponents() +{ + std::vector<Ref<Component>> components; + + size_t count; + BNComponent** list = BNComponentGetContainedComponents(m_object, &count); + + components.reserve(count); + for (size_t i = 0; i < count; i++) + { + Ref<Component> component = new Component(BNNewComponentReference(list[i])); + components.push_back(component); + } + + BNFreeComponents(list, count); + + return components; +} + + +std::vector<Ref<Function>> Component::GetContainedFunctions() +{ + std::vector<Ref<Function>> functions; + + size_t count; + BNFunction** list = BNComponentGetContainedFunctions(m_object, &count); + + functions.reserve(count); + for (size_t i = 0; i < count; i++) + { + Ref<Function> function = new Function(BNNewFunctionReference(list[i])); + functions.push_back(function); + } + + BNFreeFunctionList(list, count); + + return functions; +} + + +std::vector<Ref<Type>> Component::GetReferencedTypes() +{ + std::vector<Ref<Type>> types; + + size_t count; + BNType** list = BNComponentGetReferencedTypes(m_object, &count); + + types.reserve(count); + for (size_t i = 0; i < count; i++) + { + Ref<Type> type = new Type(BNNewTypeReference(list[i])); + types.push_back(type); + } + + BNComponentFreeReferencedTypes(list, count); + + return types; +} + + +std::vector<DataVariable> Component::GetReferencedDataVariables() +{ + vector<DataVariable> result; + + size_t count; + BNDataVariable* variables = BNComponentGetReferencedDataVariables(m_object, &count); + + result.reserve(count); + for (size_t i = 0; i < count; ++i) + { + result.emplace_back(variables[i].address, + Confidence(new Type(BNNewTypeReference(variables[i].type)), variables[i].typeConfidence), + variables[i].autoDiscovered); + } + + BNFreeDataVariables(variables, count); + return result; +} diff --git a/function.cpp b/function.cpp index 96795ef0..12c9d241 100644 --- a/function.cpp +++ b/function.cpp @@ -1446,6 +1446,24 @@ set<SSAVariable> Function::GetHighLevelILSSAVariablesIfAvailable() } +std::vector<Ref<Component>> Function::GetParentComponents() const +{ + std::vector<Ref<Component>> components; + + size_t count; + BNComponent** list = BNGetFunctionParentComponents(m_object, &count); + + components.reserve(count); + for (size_t i = 0; i < count; i++) + { + Ref<Component> component = new Component(list[i]); + components.push_back(component); + } + + return components; +} + + void Function::CreateAutoVariable( const Variable& var, const Confidence<Ref<Type>>& type, const string& name, bool ignoreDisjointUses) { diff --git a/python/__init__.py b/python/__init__.py index ef316c36..1e73f2cc 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -67,6 +67,7 @@ from .database import * from .secretsprovider import * from .typeparser import * from .typeprinter import * +from .component import * # We import each of these by name to prevent conflicts between # log.py and the function 'log' which we don't import below from .log import ( diff --git a/python/binaryview.py b/python/binaryview.py index 278cbecd..e68bf69d 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -48,6 +48,7 @@ from . import typelibrary from . import fileaccessor from . import databuffer from . import basicblock +from . import component from . import lineardisassembly from . import metadata from . import highlight @@ -228,6 +229,30 @@ class BinaryDataNotification: def section_removed(self, view: 'BinaryView', section: 'Section') -> None: pass + def component_added(self, view: 'BinaryView', _component: component.Component) -> None: + pass + + def component_removed(self, view: 'BinaryView', formerParent: component.Component, + _component: component.Component) -> None: + pass + + def component_name_updated(self, view: 'BinaryView', previous_name: str, _component: component.Component) -> None: + pass + + def component_moved(self, view: 'BinaryView', formerParent: component.Component, newParent: component.Component, + _component: component.Component) -> None: + pass + + def component_function_added(self, view: 'BinaryView', _component: component.Component, func: '_function.Function'): + pass + + def component_function_removed(self, view: 'BinaryView', _component: component.Component, + func: '_function.Function'): + pass + + + + class StringReference: _decodings = { StringType.AsciiString: "ascii", StringType.Utf8String: "utf-8", StringType.Utf16String: "utf-16", @@ -462,7 +487,12 @@ class BinaryDataNotificationCallbacks: self._cb.sectionAdded = self._cb.sectionAdded.__class__(self._section_added) self._cb.sectionUpdated = self._cb.sectionUpdated.__class__(self._section_updated) self._cb.sectionRemoved = self._cb.sectionRemoved.__class__(self._section_removed) - + self._cb.componentNameUpdated = self._cb.componentNameUpdated.__class__(self._component_name_updated) + self._cb.componentAdded = self._cb.componentAdded.__class__(self._component_added) + self._cb.componentRemoved = self._cb.componentRemoved.__class__(self._component_removed) + self._cb.componentMoved = self._cb.componentMoved.__class__(self._component_moved) + self._cb.componentFunctionAdded = self._cb.componentFunctionAdded.__class__(self._component_function_added) + self._cb.componentFunctionRemoved = self._cb.componentFunctionRemoved.__class__(self._component_function_removed) def _register(self) -> None: core.BNRegisterDataNotification(self._view.handle, self._cb) @@ -736,6 +766,78 @@ class BinaryDataNotificationCallbacks: except: log_error(traceback.format_exc()) + def _component_added(self, ctxt, view: core.BNBinaryView, _component: core.BNComponent): + try: + component_handle = core.BNNewComponentReference(_component) + assert component_handle is not None, "core.BNNewComponentReference returned None" + result = component.Component(component_handle) + self._notify.component_added(self._view, result) + except: + log_error(traceback.format_exc()) + + def _component_removed(self, ctxt, view: core.BNBinaryView, formerParent: core.BNComponent, _component: core.BNComponent): + try: + formerParent_handle = core.BNNewComponentReference(formerParent) + assert formerParent_handle is not None, "core.BNNewComponentReference returned None" + formerParentResult = component.Component(formerParent_handle) + component_handle = core.BNNewComponentReference(_component) + assert component_handle is not None, "core.BNNewComponentReference returned None" + result = component.Component(component_handle) + self._notify.component_removed(self._view, formerParentResult, result) + except: + log_error(traceback.format_exc()) + + def _component_name_updated(self, ctxt, view: core.BNBinaryView, previous_name: str, _component: core.BNComponent): + try: + component_handle = core.BNNewComponentReference(_component) + assert component_handle is not None, "core.BNNewComponentReference returned None" + result = component.Component(component_handle) + self._notify.component_name_updated(self._view, previous_name, result) + except: + log_error(traceback.format_exc()) + + def _component_moved(self, ctxt, view: core.BNBinaryView, formerParent: core.BNComponent, + newParent: core.BNComponent, _component: core.BNComponent): + try: + formerParent_handle = core.BNNewComponentReference(formerParent) + assert formerParent_handle is not None, "core.BNNewComponentReference returned None" + formerParentResult = component.Component(formerParent_handle) + newParent_handle = core.BNNewComponentReference(newParent) + assert newParent_handle is not None, "core.BNNewComponentReference returned None" + newParentResult = component.Component(newParent_handle) + component_handle = core.BNNewComponentReference(_component) + assert component_handle is not None, "core.BNNewComponentReference returned None" + result = component.Component(component_handle) + self._notify.component_moved(self._view, formerParentResult, newParentResult, result) + except: + log_error(traceback.format_exc()) + + def _component_function_added(self, ctxt, view: core.BNBinaryView, _component: core.BNComponent, + func: '_function.Function'): + try: + component_handle = core.BNNewComponentReference(_component) + assert component_handle is not None, "core.BNNewComponentReference returned None" + result = component.Component(component_handle) + function_handle = core.BNNewFunctionReference(func) + assert function_handle is not None, "core.BNNewFunctionReference returned None" + function = _function.Function(self._view, function_handle) + self._notify.component_function_added(self._view, result, function) + except: + log_error(traceback.format_exc()) + + def _component_function_removed(self, ctxt, view: core.BNBinaryView, _component: core.BNComponent, + func: '_function.Function'): + try: + component_handle = core.BNNewComponentReference(_component) + assert component_handle is not None, "core.BNNewComponentReference returned None" + result = component.Component(component_handle) + function_handle = core.BNNewFunctionReference(func) + assert function_handle is not None, "core.BNNewFunctionReference returned None" + function = _function.Function(self._view, function_handle) + self._notify.component_function_removed(self._view, result, function) + except: + log_error(traceback.format_exc()) + @property def view(self) -> 'BinaryView': return self._view @@ -5827,6 +5929,105 @@ class BinaryView: def notify_data_removed(self, offset: int, length: int) -> None: core.BNNotifyDataRemoved(self.handle, offset, length) + def get_component(self, guid: str) -> Optional[component.Component]: + """ + Lookup a Component by its Guid + + :param guid: Guid of the component to look up + :return: The Component with that Guid + """ + bn_component = core.BNGetComponentByGuid(self.handle, guid) + if bn_component is None: + return None + return component.Component(bn_component) + + def get_component_by_path(self, path: str) -> Optional[component.Component]: + """ + Lookup a Component by its pathname + + :note: This is a convenience method, and for performance-sensitive lookups, GetComponentByGuid is very highly recommended. + + Lookups are done based on the .display_name of the Component. + + All lookups are absolute from the root component, and are case-sensitive. Pathnames are delimited with "/" + + :param path: Pathname of the desired Component + :return: The Component at that pathname + :Example: + + >>> c = bv.create_component(name="MyComponent") + >>> c2 = bv.create_component(name="MySubComponent", parent=c) + >>> bv.get_component_by_path("/MyComponent/MySubComponent") == c2 + True + >>> c3 = bv.create_component(name="MySubComponent", parent=c) + >>> c3 + <Component "MySubComponent (1)" "(20712aff...")> + >>> bv.get_component_by_path("/MyComponent/MySubComponent (1)") == c3 + True + """ + if not isinstance(path, str): + raise TypeError("Pathname must be a string") + bn_component = core.BNGetComponentByPath(self.handle, path) + if bn_component is None: + return None + return component.Component(bn_component) + + @property + def root_component(self) -> component.Component: + """ + The root component for the BinaryView (read-only) + + This Component cannot be removed, and houses all unparented Components. + + :return: The root component + """ + return component.Component(core.BNGetRootComponent(self.handle)) + + def create_component(self, name: Optional[str] = None, parent: Union[component.Component, str, None] = None) -> component.Component: + """ + Create a new component with an optional name and parent. + + The `parent` argument can be either a Component or the Guid of a component that the created component will be + added as a child of + + :param name: Optional name to create the component with + :param parent: Optional parent to which the component will be added + :return: The created component + """ + + if parent: + if isinstance(parent, component.Component): + if name: + return component.Component(core.BNCreateComponentWithParentAndName(self.handle, parent.guid, name)) + else: + return component.Component(core.BNCreateComponentWithParent(self.handle, parent.guid)) + elif isinstance(parent, str): + if name: + return component.Component(core.BNCreateComponentWithParentAndName(self.handle, parent, name)) + else: + return component.Component(core.BNCreateComponentWithParent(self.handle, parent)) + else: + raise TypeError("parent can only be a Component object or string GUID representing one") + else: + if name: + return component.Component(core.BNCreateComponentWithName(self.handle, name)) + else: + return component.Component(core.BNCreateComponent(self.handle)) + + def remove_component(self, _component: Union[component.Component, str]) -> bool: + """ + Remove a component from the tree entirely. + + :param _component: Component to remove + :return: Whether the removal was successful + """ + if isinstance(_component, component.Component): + return core.BNRemoveComponent(self.handle, _component.handle) + elif isinstance(_component, str): + return core.BNRemoveComponentByGuid(self.handle, _component) + + raise TypeError("Removal is only supported with a Component or string representing its Guid") + def get_strings(self, start: Optional[int] = None, length: Optional[int] = None) -> List['StringReference']: """ ``get_strings`` returns a list of strings defined in the binary in the optional virtual address range: diff --git a/python/component.py b/python/component.py new file mode 100644 index 00000000..21942297 --- /dev/null +++ b/python/component.py @@ -0,0 +1,266 @@ + +import ctypes +import inspect +from typing import Generator, Optional, List, Tuple, Union, Mapping, Any, Dict, Iterator +from dataclasses import dataclass + +from . import binaryview + +from . import function +from . import _binaryninjacore as core +from . import types + + +class Component: + """ + Components are objects that can contain Functions and other Components. + + They can be queried for information about the functions contained within them. + + Components have a Guid, which persistent across saves and loads of the database, and should be + used for retrieving components when such is required and a reference to the Component cannot be held. + + """ + def __init__(self, handle=None): + + assert handle is not None, "Cannot create component directly, run `bv.create_component?`" + + self.handle = handle + + self.guid = core.BNComponentGetGuid(self.handle) + + def __eq__(self, other): + if not isinstance(other, Component): + return NotImplemented + return core.BNComponentsEqual(self.handle, other.handle) + + def __ne__(self, other): + if not isinstance(other, Component): + return NotImplemented + return core.BNComponentsNotEqual(self.handle, other.handle) + + def __repr__(self): + return f'<Component "{self.display_name}" "({self.guid[:8]}...")>' + + def __del__(self): + core.BNFreeComponent(self.handle) + + def __str__(self): + return self._sprawl_component(self) + + def _sprawl_component(self, c, depth=1, out=None): + """ + Recursive quick function to print out the component's tree of items + + :param c: Current cycle's component. On initial call, pass `self` + :param depth: Current tree depth. + :param out: Current text + :return: + """ + _out = ([repr(c)] if not out else out.split('\n')) + [(' ' * depth + repr(f)) for f in c.functions] + _out += [' ' * (depth+1) + repr(i) for i in (c.get_referenced_data_variables() + c.get_referenced_types())] + for i in c.components: + _out.append(' ' * depth + repr(i)) + _out = self._sprawl_component(i, depth+1, '\n'.join(_out)).split('\n') + return '\n'.join(_out) + + def add_function(self, func: 'function.Function') -> bool: + """ + Add function to this component. + + :param func: Function to add + :return: True if function was successfully added. + """ + return core.BNComponentAddFunctionReference(self.handle, func.handle) + + def contains_function(self, func: 'function.Function') -> bool: + """ + Check whether this component contains a function. + + :param func: Function to check + :return: True if this component contains the function. + """ + return core.BNComponentContainsFunction(self.handle, func.handle) + + def add_component(self, component: 'Component') -> bool: + """ + Move component to this component. This will remove it from the old parent. + + :param component: Component to add to this component. + :return: True if the component was successfully moved to this component + """ + return core.BNComponentAddComponent(self.handle, component.handle) + + def remove_function(self, func: 'function.Function') -> bool: + """ + Remove function from this component. + + :param func: Function to remove + :return: True if function was successfully removed. + """ + return core.BNComponentRemoveFunctionReference(self.handle, func.handle) + + def remove_component(self, component: 'Component') -> bool: + """ + Remove a component from the current component, moving it to the root. + + This function has no effect when used from the root component. + Use `BinaryView.remove_component` to Remove a component from the tree entirely. + + :param component: Component to remove + :return: + """ + + return self.view.root_component.add_component(component) + + def contains_component(self, component: 'Component') -> bool: + """ + Check whether this component contains a component. + + :param component: Component to check + :return: True if this component contains the component. + """ + return core.BNComponentContainsComponent(self.handle, component.handle) + + @property + def display_name(self) -> str: + """Original Name of the component (read-only)""" + return core.BNComponentGetDisplayName(self.handle) + + @property + def name(self) -> str: + """Original name set for this component + + :note: The `.display_name` property should be used for `bv.get_component_by_path()` lookups. + + This can differ from the .display_name property if one of its sibling components has the same .original_name; In that + case, .name will be an automatically generated unique name (e.g. "MyComponentName (1)") while .original_name will + remain what was originally set (e.g. "MyComponentName") + + If this component has a duplicate name and is moved to a component where none of its siblings share its name, + the .name property will return the original "MyComponentName" + """ + return core.BNComponentGetOriginalName(self.handle) + + @name.setter + def name(self, _name): + core.BNComponentSetName(self.handle, _name) + + @property + def parent(self) -> Optional['Component']: + """ + The component that contains this component, if it exists. + """ + bn_component = core.BNComponentGetParent(self.handle) + if bn_component is not None: + return Component(bn_component) + return None + + @property + def view(self): + bn_binaryview = core.BNComponentGetView(self.handle) + if bn_binaryview is not None: + return binaryview.BinaryView(handle=bn_binaryview) + return None + + @property + def components(self) -> Iterator['Component']: + """ + ``components`` is an iterator for all Components contained within this Component + + :return: An iterator containing Components + :rtype: SubComponentIterator + :Example: + + >>> for subcomp in component.components: + ... print(repr(component)) + """ + + @dataclass + class SubComponentIterator: + comp: Component + + def __iter__(self): + count = ctypes.c_ulonglong(0) + bn_components = core.BNComponentGetContainedComponents(self.comp.handle, count) + try: + for i in range(count.value): + yield Component(core.BNNewComponentReference(bn_components[i])) + finally: + core.BNFreeComponents(bn_components, count.value) + + return iter(SubComponentIterator(self)) + + @property + def functions(self) -> Iterator['function.Function']: + """ + ``functions`` is an iterator for all Functions contained within this Component + + :return: An iterator containing Components + :rtype: ComponentIterator + :Example: + + >>> for func in component.functions: + ... print(func.name) + """ + @dataclass + class FunctionIterator: + view: 'binaryview.BinaryView' + comp: Component + + def __iter__(self): + count = ctypes.c_ulonglong(0) + bn_functions = core.BNComponentGetContainedFunctions(self.comp.handle, count) + try: + for i in range(count.value): + bn_function = core.BNNewFunctionReference(bn_functions[i]) + yield function.Function(self.view, bn_function) + finally: + core.BNFreeFunctionList(bn_functions, count.value) + + return iter(FunctionIterator(self.view, self)) + + def get_referenced_data_variables(self, recursive=False): + """ + Get data variables referenced by this component + + :param recursive: Optional; Get all DataVariables referenced by this component and subcomponents. + :return: List of DataVariables + """ + data_vars = [] + count = ctypes.c_ulonglong(0) + if recursive: + bn_data_vars = core.BNComponentGetReferencedDataVariablesRecursive(self.handle, count) + else: + bn_data_vars = core.BNComponentGetReferencedDataVariables(self.handle, count) + try: + for i in range(count.value): + bn_data_var = bn_data_vars[i] + data_var = binaryview.DataVariable.from_core_struct(bn_data_var, self.view) + data_vars.append(data_var) + finally: + core.BNFreeDataVariables(bn_data_vars, count.value) + return data_vars + + def get_referenced_types(self, recursive=False): + """ + Get Types referenced by this component + + :param recursive: Optional; Get all Types referenced by this component and subcomponents. + :return: List of Types + """ + _types = [] + count = ctypes.c_ulonglong(0) + + if recursive: + bn_types = core.BNComponentGetReferencedTypesRecursive(self.handle, count) + else: + bn_types = core.BNComponentGetReferencedTypes(self.handle, count) + + try: + for i in range(count.value): + _types.append(types.Type(core.BNNewTypeReference(bn_types[i]))) + finally: + core.BNComponentFreeReferencedTypes(bn_types, count.value) + + return _types diff --git a/python/function.py b/python/function.py index 93ed8b49..d093c076 100644 --- a/python/function.py +++ b/python/function.py @@ -61,6 +61,7 @@ from .variable import ( ) from . import decorators from .enums import RegisterValueType +from . import component ExpressionIndex = int InstructionIndex = int @@ -2196,6 +2197,15 @@ class Function: core.BNFreeTagList(tags, count.value) return result + @property + def parent_components(self): + _components = [] + count = ctypes.c_ulonglong(0) + bn_components = core.BNGetFunctionParentComponents(self.handle, count) + for i in range(count.value): + _components.append(component.Component(self.view, bn_components[i])) + return _components + def get_lifted_il_at( self, addr: int, arch: Optional['architecture.Architecture'] = None ) -> Optional['lowlevelil.LowLevelILInstruction']: |
