diff options
| -rw-r--r-- | binaryninjaapi.h | 535 | ||||
| -rw-r--r-- | binaryninjacore.h | 104 | ||||
| -rw-r--r-- | binaryview.cpp | 240 | ||||
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/binaryview.py | 463 | ||||
| -rw-r--r-- | python/typearchive.py | 758 | ||||
| -rw-r--r-- | typearchive.cpp | 635 | ||||
| -rw-r--r-- | typecontainer.cpp | 45 | ||||
| -rw-r--r-- | ui/commands.h | 4 | ||||
| -rw-r--r-- | ui/createstructdialog.h | 8 | ||||
| -rw-r--r-- | ui/typebrowser.h | 76 | ||||
| -rw-r--r-- | ui/typedialog.h | 12 | ||||
| -rw-r--r-- | ui/uitypes.h | 1 |
13 files changed, 2865 insertions, 17 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 66bf0c21..b0794456 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -3020,6 +3020,7 @@ namespace BinaryNinja { class Section; class Segment; class Component; + class TypeArchive; /*! @@ -3073,6 +3074,7 @@ namespace BinaryNinja { static void ComponentFunctionRemovedCallback(void* ctxt, BNBinaryView* data, BNComponent* component, BNFunction* function); static void ComponentDataVariableAddedCallback(void* ctxt, BNBinaryView* data, BNComponent* component, BNDataVariable* var); static void ComponentDataVariableRemovedCallback(void* ctxt, BNBinaryView* data, BNComponent* component, BNDataVariable* var); + static void ExternalLibraryAddedCallback(void* ctxt, BNBinaryView* data, BNExternalLibrary* library); static void ExternalLibraryUpdatedCallback(void* ctxt, BNBinaryView* data, BNExternalLibrary* library); static void ExternalLibraryRemovedCallback(void* ctxt, BNBinaryView* data, BNExternalLibrary* library); @@ -3080,6 +3082,11 @@ namespace BinaryNinja { static void ExternalLocationUpdatedCallback(void* ctxt, BNBinaryView* data, BNExternalLocation* location); static void ExternalLocationRemovedCallback(void* ctxt, BNBinaryView* data, BNExternalLocation* location); + static void TypeArchiveAttachedCallback(void* ctxt, BNBinaryView* data, const char* id, const char* path); + static void TypeArchiveDetachedCallback(void* ctxt, BNBinaryView* data, const char* id, const char* path); + static void TypeArchiveConnectedCallback(void* ctxt, BNBinaryView* data, BNTypeArchive* archive); + static void TypeArchiveDisconnectedCallback(void* ctxt, BNBinaryView* data, BNTypeArchive* archive); + public: enum NotificationType : uint64_t @@ -3129,6 +3136,10 @@ namespace BinaryNinja { ExternalLocationAdded = 1ULL << 42, ExternalLocationUpdated = 1ULL << 43, ExternalLocationRemoved = 1ULL << 44, + TypeArchiveAttached = 1ULL << 45, + TypeArchiveDetached = 1ULL << 46, + TypeArchiveConnected = 1ULL << 47, + TypeArchiveDisconnected = 1ULL << 48, BinaryDataUpdates = DataWritten | DataInserted | DataRemoved, FunctionLifetime = FunctionAdded | FunctionRemoved, @@ -3146,7 +3157,8 @@ namespace BinaryNinja { SegmentUpdates = SegmentLifetime | SegmentUpdated, SectionLifetime = SectionAdded | SectionRemoved, SectionUpdates = SectionLifetime | SectionUpdated, - ComponentUpdates = ComponentNameUpdated | ComponentAdded | ComponentRemoved | ComponentMoved | ComponentFunctionAdded | ComponentFunctionRemoved | ComponentDataVariableAdded | ComponentDataVariableRemoved + ComponentUpdates = ComponentNameUpdated | ComponentAdded | ComponentRemoved | ComponentMoved | ComponentFunctionAdded | ComponentFunctionRemoved | ComponentDataVariableAdded | ComponentDataVariableRemoved, + TypeArchiveUpdates = TypeArchiveAttached | TypeArchiveDetached | TypeArchiveConnected | TypeArchiveDisconnected }; using NotificationTypes = uint64_t; @@ -3471,6 +3483,29 @@ namespace BinaryNinja { (void)data; (void)location; } + + virtual void OnTypeArchiveAttached(BinaryView* data, const std::string& id, const std::string& path) + { + (void)data; + (void)id; + (void)path; + } + virtual void OnTypeArchiveDetached(BinaryView* data, const std::string& id, const std::string& path) + { + (void)data; + (void)id; + (void)path; + } + virtual void OnTypeArchiveConnected(BinaryView* data, TypeArchive* archive) + { + (void)data; + (void)archive; + } + virtual void OnTypeArchiveDisconnected(BinaryView* data, TypeArchive* archive) + { + (void)data; + (void)archive; + } }; /*! @@ -4083,6 +4118,7 @@ namespace BinaryNinja { class Component; class DebugInfo; class TypeLibrary; + class TypeArchive; class QueryMetadataException : public ExceptionWithStackTrace { @@ -5951,6 +5987,92 @@ namespace BinaryNinja { or std::nullopt if it was not imported */ std::optional<std::pair<Ref<TypeLibrary>, QualifiedName>> LookupImportedTypeLibrary(const QualifiedName& name); + /*! + Attach a given type archive to the binary view. No types will actually be associated by calling this, just they + will become available. + \param id Expected id of archive + \param path Path to archive + */ + Ref<TypeArchive> AttachTypeArchive(const std::string& id, const std::string& path); + /*! + Detach from a type archive, breaking all associations to types with the archive + \param id Id of archive to detach + */ + void DetachTypeArchive(const std::string& id); + /*! + Look up a connected archive by its id + \param id Id of archive + \return Archive, if one exists with that id. Otherwise nullptr + */ + Ref<TypeArchive> GetTypeArchive(const std::string& id) const; + /*! + Get all attached type archives + \return All archives + */ + std::unordered_map<std::string, std::string> GetTypeArchives() const; + /*! + Look up the path for an attached (but not necessarily connected) type archive by its id + \param id Id of archive + \return Archive path, if it is attached. Otherwise nullopt. + */ + std::optional<std::string> GetTypeArchivePath(const std::string& id) const; + /*! + Get a list of all available type names in all connected archives, and their archive/type id pair + \return All type names in a map + */ + std::unordered_map<QualifiedName, std::map<std::string, std::string>> GetTypeArchiveTypeNames() const; + + /*! + Get a list of all types in the analysis that are associated with a specific type archive + \return Map of all analysis types to their corresponding archive id + */ + std::unordered_map<std::string, std::pair<std::string, std::string>> GetAssociatedTypeArchiveTypes() const; + /*! + Get a list of all types in the analysis that are associated with a specific type archive + \return Map of all analysis types to their corresponding archive id + */ + std::unordered_map<std::string, std::string> GetAssociatedTypesFromArchive(const std::string& archive) const; + /*! + Determine the target archive / type id of a given analysis type + \param id Id of analysis type + \return Pair of archive id and archive type id, if this type is associated. std::nullopt otherwise. + */ + std::optional<std::pair<std::string, std::string>> GetAssociatedTypeArchiveTypeTarget(const std::string& id) const; + /*! + Determine the local source type for a given archive type + \param archiveId Id of target archive + \param archiveTypeId Id of target archive type + \return Id of source analysis type, if this type is associated. std::nullopt otherwise. + */ + std::optional<std::string> GetAssociatedTypeArchiveTypeSource(const std::string& archiveId, const std::string& archiveTypeId) const; + /*! + Get the current status of any changes pending in a given type + \param id Id of type in analysis + \return Status of type + */ + BNSyncStatus GetTypeArchiveSyncStatus(const std::string& typeId) const; + /*! + Disassociate an associated type, so that it will no longer receive updates from its connected type archive + \param typeId Id of type in analysis + \return True if successful + */ + bool DisassociateTypeArchiveType(const std::string& typeId); + /*! + Pull a collection of types from a type archive, associating with them and any dependencies + \param[in] archiveId Id of archive + \param[in] archiveTypeIds Ids of desired types + \param[out] updatedTypes List of types that were updated + \return True if successful + */ + bool PullTypeArchiveTypes(const std::string& archiveId, const std::unordered_set<std::string>& archiveTypeIds, std::unordered_map<std::string, std::string>& updatedTypes); + /*! + Push a collection of types, and all their dependencies, into a type archive + \param[in] archiveId Id of archive + \param[in] typeIds List of ids of types in analysis + \param[out] updatedTypes List of types that were updated + \return True if successful + */ + bool PushTypeArchiveTypes(const std::string& archiveId, const std::unordered_set<std::string>& typeIds, std::unordered_map<std::string, std::string>& updatedTypes); bool FindNextData( uint64_t start, const DataBuffer& data, uint64_t& result, BNFindFlag flags = FindCaseSensitive); @@ -16514,6 +16636,395 @@ namespace BinaryNinja { void Finalize(); }; + class TypeArchive; + class TypeArchiveNotification + { + BNTypeArchiveNotification m_callbacks; + + static void OnTypeAddedCallback(void* ctx, BNTypeArchive* archive, const char* id, BNType* definition); + static void OnTypeUpdatedCallback(void* ctx, BNTypeArchive* archive, const char* id, BNType* oldDefinition, BNType* newDefinition); + static void OnTypeRenamedCallback(void* ctx, BNTypeArchive* archive, const char* id, const BNQualifiedName* oldName, const BNQualifiedName* newName); + static void OnTypeDeletedCallback(void* ctx, BNTypeArchive* archive, const char* id, BNType* definition); + + public: + TypeArchiveNotification(); + virtual ~TypeArchiveNotification() = default; + + BNTypeArchiveNotification* GetCallbacks() { return &m_callbacks; } + + /*! + Called when a type is added to the archive + \param archive + \param id Id of type added + \param definition Definition of type + */ + virtual void OnTypeAdded(Ref<TypeArchive> archive, const std::string& id, Ref<Type> definition) + { + (void)archive; + (void)id; + } + + /*! + Called when a type in the archive is updated to a new definition + \param archive + \param id Id of type + \param oldDefinition Previous definition + \param newDefinition Current definition + */ + virtual void OnTypeUpdated(Ref<TypeArchive> archive, const std::string& id, Ref<Type> oldDefinition, Ref<Type> newDefinition) + { + (void)archive; + (void)id; + (void)oldDefinition; + (void)newDefinition; + } + + /*! + Called when a type in the archive is renamed + \param archive + \param id Type id + \param oldName Previous name + \param newName Current name + */ + virtual void OnTypeRenamed(Ref<TypeArchive> archive, const std::string& id, const QualifiedName& oldName, const QualifiedName& newName) + { + (void)archive; + (void)oldName; + (void)newName; + } + + /*! + Called when a type in the archive is deleted from the archive + \param archive + \param id Id of type deleted + \param definition Definition of type deleted + */ + virtual void OnTypeDeleted(Ref<TypeArchive> archive, const std::string& id, Ref<Type> definition) + { + (void)archive; + (void)id; + (void)definition; + } + }; + + /*! + \ingroup binaryview + */ + class TypeArchive: public CoreRefCountObject<BNTypeArchive, BNNewTypeArchiveReference, BNFreeTypeArchiveReference> + { + public: + TypeArchive(BNTypeArchive* archive); + + /*! + Open the type archive at the given path, if it exists. + \param path Path to type archive file + \return Type archive, or nullptr if it could not be loaded. + */ + static Ref<TypeArchive> Open(const std::string& path); + + /*! + Create a type archive at the given path. + \param path Path to type archive file + \param platform Relevant platform for types in the archive + \return Type archive, or nullptr if it could not be loaded. + */ + static Ref<TypeArchive> Create(const std::string& path, Ref<Platform> platform); + + /*! + Create a type archive at the given path with a manually-specified id. + \note You probably want to use Create() and let BN handle picking an id for you. + \param path Path to type archive file + \param platform Relevant platform for types in the archive + \param id Assigned id for the type archive + \return Type archive, or nullptr if it could not be created. + */ + static Ref<TypeArchive> CreateWithId(const std::string& path, Ref<Platform> platform, const std::string& id); + + /*! + Get a reference to the type archive with the known id, if one exists. + \param id Type archive id + \return Type archive, or nullptr if it could not be found. + */ + static Ref<TypeArchive> LookupById(const std::string& id); + + /*! + Close a type archive, disconnecting it from any active views and closing any open file handles + \param archive Type Archive to close + */ + static void Close(Ref<TypeArchive> archive); + + /*! + Determine if a file is a Type Archive + \param path File path + \return True if it's a type archive + */ + static bool IsTypeArchive(const std::string& path); + + /*! + Get the unique id associated with this type archive + \return The id + */ + std::string GetId() const; + + /*! + Get the path to the type archive + \return The path + */ + std::string GetPath() const; + + /*! + Get the associated Platform for a Type Archive + \return Platform + */ + Ref<Platform> GetPlatform() const; + + /*! + Get the id of the current snapshot in the type archive + \throws DatabaseException if an exception occurs + \return Snapshot id + */ + std::string GetCurrentSnapshotId() const; + + /*! + Revert the type archive's current snapshot to the given snapshot + \param id Snapshot id + */ + void SetCurrentSnapshot(const std::string& id); + + /*! + Get a list of every snapshot's id + \throws DatabaseException if an exception occurs + \return All ids (including the empty first snapshot) + */ + std::vector<std::string> GetAllSnapshotIds() const; + + /*! + Get the ids of the parents to the given snapshot + \param id Child snapshot id + \throws DatabaseException if an exception occurs + \return Parent snapshot ids, or empty vector if the snapshot is a root + */ + std::vector<std::string> GetSnapshotParentIds(const std::string& id) const; + + /*! + Get the ids of the children to the given snapshot + \param id Parent snapshot id + \throws DatabaseException if an exception occurs + \return Child snapshot ids, or empty vector if the snapshot is a leaf + */ + std::vector<std::string> GetSnapshotChildIds(const std::string& id) const; + + class TypeContainer GetTypeContainer() const; + + /*! + Add named types to the type archive. Types must have all dependant named + types prior to being added, or this function will fail. + Types already existing with any added names will be overwritten. + \param name Name of new type + \param types Type definitions + \throws DatabaseException if an exception occurs + \return True if the types were added + */ + bool AddTypes(const std::vector<QualifiedNameAndType>& types); + + /*! + Change the name of an existing type in the type archive. + \param id Type id + \param newName New type name + \throws DatabaseException if an exception occurs + \return True if successful + */ + bool RenameType(const std::string& id, const QualifiedName& newName); + + /*! + Delete an existing type in the type archive. + \param id Type id + \throws DatabaseException if an exception occurs + \return True if successful + */ + bool DeleteType(const std::string& id); + + /*! + Retrieve a stored type in the archive by id + \param id Type id + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return Type, if it exists. Otherwise nullptr + */ + Ref<Type> GetTypeById(const std::string& id, std::string snapshot = "") const; + + /*! + Retrieve a stored type in the archive + \param name Type name + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return Type, if it exists. Otherwise nullptr + */ + Ref<Type> GetTypeByName(const QualifiedName& name, std::string snapshot = "") const; + + /*! + Retrieve a type's id by its name + \param name Type name + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return Type id, if it exists. Otherwise empty string + */ + std::string GetTypeId(const QualifiedName& name, std::string snapshot = "") const; + + /*! + Retrieve a type's name by its id + \param id Type id + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return Type name, if it exists. Otherwise empty string + */ + QualifiedName GetTypeName(const std::string& id, std::string snapshot = "") const; + + /*! + Retrieve all stored types in the archive + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return All types + */ + std::unordered_map<std::string, QualifiedNameAndType> GetTypes(std::string snapshot = "") const; + + /*! + Get a list of all types' ids currently in the archive + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return All type ids + */ + std::vector<std::string> GetTypeIds(std::string snapshot = "") const; + + /*! + Get a list of all types' names currently in the archive + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return All type names + */ + std::vector<QualifiedName> GetTypeNames(std::string snapshot = "") const; + + /*! + Get a list of all types' names and ids currently in the archive + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return All type names and ids + */ + std::unordered_map<std::string, QualifiedName> GetTypeNamesAndIds(std::string snapshot = "") const; + + /*! + Get all types a given type references directly + \param id Source type id + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return Target type ids + */ + std::unordered_set<std::string> GetOutgoingDirectTypeReferences(const std::string& id, std::string snapshot = "") const; + + /*! + Get all types a given type references, and any types that the referenced types reference + \param id Source type id + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return Target type ids + */ + std::unordered_set<std::string> GetOutgoingRecursiveTypeReferences(const std::string& id, std::string snapshot = "") const; + + /*! + Get all types that reference a given type + \param id Target type id + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return Source type ids + */ + std::unordered_set<std::string> GetIncomingDirectTypeReferences(const std::string& id, std::string snapshot = "") const; + + /*! + Get all types that reference a given type, and all types that reference them, recursively + \param id Target type id + \param snapshot Snapshot id to search for types, or empty string to search the latest snapshot + \throws DatabaseException if an exception occurs + \return Source type ids + */ + std::unordered_set<std::string> GetIncomingRecursiveTypeReferences(const std::string& id, std::string snapshot = "") const; + + /*! + Do some function in a transaction making a new snapshot whose id is passed to func. If func throws, + the transaction will be rolled back and the snapshot will not be created. + \param func Function to call + \param parents Parent snapshot ids + \return Created snapshot id + */ + std::string NewSnapshotTransaction(std::function<void(const std::string& id)> func, const std::vector<std::string>& parents); + + /*! + Register a notification listener + \param notification Object to receive notifications + */ + void RegisterNotification(TypeArchiveNotification* notification); + + /*! + Unregister a notification listener + \param notification Object to no longer receive notifications + */ + void UnregisterNotification(TypeArchiveNotification* notification); + + /*! + Store a key/value pair in the archive's metadata storage + \param key Metadata key + \param value Metadata value + \throws DatabaseException if an exception occurs + */ + void StoreMetadata(const std::string& key, Ref<Metadata> value); + + /*! + Look up a metadata entry in the archive + \param key Metadata key + \return Metadata value, if it exists. Otherwise, nullptr + */ + Ref<Metadata> QueryMetadata(const std::string& key) const; + + /*! + Delete a given metadata entry in the archive + \param key Metadata key + \throws DatabaseException if an exception occurs + */ + void RemoveMetadata(const std::string& key); + + /*! + Turn a given snapshot into a data stream + \param snapshot Snapshot id + \return Buffer containing serialized snapshot data + */ + DataBuffer SerializeSnapshot(const std::string& snapshot) const; + + /*! + Take a serialized snapshot data stream and create a new snapshot from it + \param data Snapshot data + \return String of created snapshot id + */ + std::string DeserializeSnapshot(const DataBuffer& data); + + /*! + Merge two snapshots in the archive to produce a new snapshot + \param[in] baseSnapshot Common ancestor of snapshots + \param[in] firstSnapshot First snapshot to merge + \param[in] secondSnapshot Second snapshot to merge + \param[in] mergeConflictsIn Map of resolutions for all conflicting types, id <-> target snapshot + \param[out] mergeConflictsOut List of conflicting type ids + \param[in] progress Function to call for progress updates + \return Snapshot id, if merge was successful. std::nullopt, otherwise + */ + std::optional<std::string> MergeSnapshots( + const std::string& baseSnapshot, + const std::string& firstSnapshot, + const std::string& secondSnapshot, + const std::unordered_map<std::string, std::string>& mergeConflictsIn, + std::unordered_set<std::string>& mergeConflictsOut, + std::function<bool(size_t, size_t)> progress + ); + }; + /*! A TypeContainer is a generic interface to access various Binary Ninja models that contain types. Types are stored with both a unique id and a unique name. @@ -16526,7 +17037,6 @@ namespace BinaryNinja { public: explicit TypeContainer(BNTypeContainer* container); - explicit TypeContainer(TypeContainer&& other); /*! Get the Type Container for a given BinaryView \param data BinaryView source @@ -16540,6 +17050,12 @@ namespace BinaryNinja { */ TypeContainer(Ref<TypeLibrary> library); + + /*! Get the Type Container for a Type Archive + \param archive TypeArchive source + */ + TypeContainer(Ref<TypeArchive> archive); + /*! Get the Type Container for a Platform \param platform Platform source */ @@ -16547,6 +17063,7 @@ namespace BinaryNinja { ~TypeContainer(); TypeContainer(const TypeContainer& other); + TypeContainer(TypeContainer&& other); TypeContainer& operator=(const TypeContainer& other); TypeContainer& operator=(TypeContainer&& other); bool operator==(const TypeContainer& other) const { return GetId() == other.GetId(); } @@ -16673,6 +17190,20 @@ namespace BinaryNinja { */ std::optional<std::unordered_map<std::string, QualifiedName>> GetTypeNamesAndIds() const; + /*! Parse a single type and name from a string containing their definition, + with knowledge of the types in the Type Container. + + \param source Source code to parse + \param result Reference into which the resulting type and name will be written + \param errors Reference to a list into which any parse errors will be written + \return True if parsing was successful + */ + bool ParseTypeString( + const std::string& source, + QualifiedNameAndType& result, + std::vector<TypeParserError>& errors + ); + /*! Parse an entire block of source into types, variables, and functions, with knowledge of the types in the Type Container. diff --git a/binaryninjacore.h b/binaryninjacore.h index 90eaaf00..cb742dfc 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -134,6 +134,8 @@ #define BNDB_SUFFIX "bndb" #define BNDB_EXT ("." BNDB_SUFFIX) +#define BNTA_SUFFIX "bnta" +#define BNTA_EXT ("." BNTA_SUFFIX) // The BN_DECLARE_CORE_ABI_VERSION must be included in native plugin modules. If // the ABI version is not declared, the core will not load the plugin. @@ -266,6 +268,7 @@ extern "C" typedef struct BNSecretsProvider BNSecretsProvider; typedef struct BNLogger BNLogger; typedef struct BNSymbolQueue BNSymbolQueue; + typedef struct BNTypeArchive BNTypeArchive; typedef struct BNTypeContainer BNTypeContainer; typedef struct BNProject BNProject; typedef struct BNProjectFile BNProjectFile; @@ -1508,6 +1511,10 @@ extern "C" void (*externalLocationAdded)(void* ctxt, BNBinaryView* data, BNExternalLocation* location); void (*externalLocationUpdated)(void* ctxt, BNBinaryView* data, BNExternalLocation* location); void (*externalLocationRemoved)(void* ctxt, BNBinaryView* data, BNExternalLocation* location); + void (*typeArchiveAttached)(void* ctxt, BNBinaryView* view, const char* id, const char* path); + void (*typeArchiveDetached)(void* ctxt, BNBinaryView* view, const char* id, const char* path); + void (*typeArchiveConnected)(void* ctxt, BNBinaryView* view, BNTypeArchive* archive); + void (*typeArchiveDisconnected)(void* ctxt, BNBinaryView* view, BNTypeArchive* archive); } BNBinaryDataNotification; typedef struct BNProjectNotification @@ -3093,6 +3100,15 @@ extern "C" void (*licenseStatusChanged)(void* ctxt, bool stillValid); } BNEnterpriseServerCallbacks; + typedef struct BNTypeArchiveNotification + { + void* context; + void (*typeAdded)(void* ctxt, BNTypeArchive* archive, const char* id, BNType* definition); + void (*typeUpdated)(void* ctxt, BNTypeArchive* archive, const char* id, BNType* oldDefinition, BNType* newDefinition); + void (*typeRenamed)(void* ctxt, BNTypeArchive* archive, const char* id, const BNQualifiedName* oldName, const BNQualifiedName* newName); + void (*typeDeleted)(void* ctxt, BNTypeArchive* archive, const char* id, BNType* definition); + } BNTypeArchiveNotification; + typedef enum BNTypeContainerType { AnalysisTypeContainerType, @@ -3104,6 +3120,17 @@ extern "C" PlatformTypeContainerType, } BNTypeContainerType; + typedef enum BNSyncStatus + { + NotSyncedSyncStatus, + NoChangesSyncStatus, + UnknownSyncStatus, + CanPushSyncStatus, + CanPullSyncStatus, + CanPushAndPullSyncStatus, + ConflictSyncStatus + } BNSyncStatus; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI void BNFreeString(char* str); BINARYNINJACOREAPI char** BNAllocStringList(const char** contents, size_t size); @@ -4690,6 +4717,10 @@ extern "C" BINARYNINJACOREAPI bool BNTypeContainerGetTypeIds(BNTypeContainer* container, char*** typeIds, size_t* count); BINARYNINJACOREAPI bool BNTypeContainerGetTypeNames(BNTypeContainer* container, BNQualifiedName** typeNames, size_t* count); BINARYNINJACOREAPI bool BNTypeContainerGetTypeNamesAndIds(BNTypeContainer* container, char*** typeIds, BNQualifiedName** typeNames, size_t* count); + BINARYNINJACOREAPI bool BNTypeContainerParseTypeString(BNTypeContainer* container, + const char* source, BNQualifiedNameAndType* result, + BNTypeParserError** errors, size_t* errorCount + ); BINARYNINJACOREAPI bool BNTypeContainerParseTypesFromSource(BNTypeContainer* container, const char* source, const char* fileName, const char* const* options, size_t optionCount, @@ -6851,6 +6882,79 @@ extern "C" BINARYNINJACOREAPI bool BNCoreEnumToString(const char* enumName, size_t value, char** result); BINARYNINJACOREAPI bool BNCoreEnumFromString(const char* enumName, const char* value, size_t* result); + // Type Archives + BINARYNINJACOREAPI BNTypeArchive* BNNewTypeArchiveReference(BNTypeArchive* archive); + BINARYNINJACOREAPI void BNFreeTypeArchiveReference(BNTypeArchive* archive); + BINARYNINJACOREAPI void BNFreeTypeArchiveList(BNTypeArchive** archives, size_t count); + BINARYNINJACOREAPI BNTypeArchive* BNOpenTypeArchive(const char* path); + BINARYNINJACOREAPI BNTypeArchive* BNCreateTypeArchive(const char* path, BNPlatform* platform); + BINARYNINJACOREAPI BNTypeArchive* BNCreateTypeArchiveWithId(const char* path, BNPlatform* platform, const char* id); + BINARYNINJACOREAPI BNTypeArchive* BNLookupTypeArchiveById(const char* id); + BINARYNINJACOREAPI void BNCloseTypeArchive(BNTypeArchive* archive); + BINARYNINJACOREAPI bool BNIsTypeArchive(const char* path); + BINARYNINJACOREAPI char* BNGetTypeArchiveId(BNTypeArchive* archive); + BINARYNINJACOREAPI char* BNGetTypeArchivePath(BNTypeArchive* archive); + BINARYNINJACOREAPI BNPlatform* BNGetTypeArchivePlatform(BNTypeArchive* archive); + BINARYNINJACOREAPI char* BNGetTypeArchiveCurrentSnapshotId(BNTypeArchive* archive); + BINARYNINJACOREAPI void BNSetTypeArchiveCurrentSnapshot(BNTypeArchive* archive, const char* id); + BINARYNINJACOREAPI char** BNGetTypeArchiveAllSnapshotIds(BNTypeArchive* archive, size_t* count); + BINARYNINJACOREAPI char** BNGetTypeArchiveSnapshotParentIds(BNTypeArchive* archive, const char* id, size_t* count); + BINARYNINJACOREAPI char** BNGetTypeArchiveSnapshotChildIds(BNTypeArchive* archive, const char* id, size_t* count); + BINARYNINJACOREAPI BNTypeContainer* BNGetTypeArchiveTypeContainer(BNTypeArchive* archive); + BINARYNINJACOREAPI bool BNAddTypeArchiveTypes(BNTypeArchive* archive, const BNQualifiedNameAndType* types, size_t count); + BINARYNINJACOREAPI bool BNRenameTypeArchiveType(BNTypeArchive* archive, const char* id, const BNQualifiedName* newName); + BINARYNINJACOREAPI bool BNDeleteTypeArchiveType(BNTypeArchive* archive, const char* id); + BINARYNINJACOREAPI BNType* BNGetTypeArchiveTypeById(BNTypeArchive* archive, const char* id, const char* snapshot); + BINARYNINJACOREAPI BNType* BNGetTypeArchiveTypeByName(BNTypeArchive* archive, const BNQualifiedName* name, const char* snapshot); + BINARYNINJACOREAPI char* BNGetTypeArchiveTypeId(BNTypeArchive* archive, const BNQualifiedName* name, const char* snapshot); + BINARYNINJACOREAPI BNQualifiedName BNGetTypeArchiveTypeName(BNTypeArchive* archive, const char* id, const char* snapshot); + BINARYNINJACOREAPI BNQualifiedNameTypeAndId* BNGetTypeArchiveTypes(BNTypeArchive* archive, const char* snapshot, size_t* count); + BINARYNINJACOREAPI char** BNGetTypeArchiveTypeIds(BNTypeArchive* archive, const char* snapshot, size_t* count); + BINARYNINJACOREAPI BNQualifiedName* BNGetTypeArchiveTypeNames(BNTypeArchive* archive, const char* snapshot, size_t* count); + BINARYNINJACOREAPI bool BNGetTypeArchiveTypeNamesAndIds(BNTypeArchive* archive, const char* snapshot, BNQualifiedName** names, char*** ids, size_t* count); + BINARYNINJACOREAPI char** BNGetTypeArchiveOutgoingDirectTypeReferences(BNTypeArchive* archive, const char* id, const char* snapshot, size_t* count); + BINARYNINJACOREAPI char** BNGetTypeArchiveOutgoingRecursiveTypeReferences(BNTypeArchive* archive, const char* id, const char* snapshot, size_t* count); + BINARYNINJACOREAPI char** BNGetTypeArchiveIncomingDirectTypeReferences(BNTypeArchive* archive, const char* id, const char* snapshot, size_t* count); + BINARYNINJACOREAPI char** BNGetTypeArchiveIncomingRecursiveTypeReferences(BNTypeArchive* archive, const char* id, const char* snapshot, size_t* count); + BINARYNINJACOREAPI char* BNTypeArchiveNewSnapshotTransaction(BNTypeArchive* archive, bool(*func)(void* context, const char* id), void* context, const char* const* parents, size_t parentCount); + BINARYNINJACOREAPI void BNRegisterTypeArchiveNotification(BNTypeArchive* archive, BNTypeArchiveNotification* notification); + BINARYNINJACOREAPI void BNUnregisterTypeArchiveNotification(BNTypeArchive* archive, BNTypeArchiveNotification* notification); + BINARYNINJACOREAPI bool BNTypeArchiveStoreMetadata(BNTypeArchive* archive, const char* key, BNMetadata* value); + BINARYNINJACOREAPI BNMetadata* BNTypeArchiveQueryMetadata(BNTypeArchive* archive, const char* key); + BINARYNINJACOREAPI bool BNTypeArchiveRemoveMetadata(BNTypeArchive* archive, const char* key); + BINARYNINJACOREAPI BNDataBuffer* BNTypeArchiveSerializeSnapshot(BNTypeArchive* archive, const char* snapshot); + BINARYNINJACOREAPI char* BNTypeArchiveDeserializeSnapshot(BNTypeArchive* archive, BNDataBuffer* buffer); + BINARYNINJACOREAPI bool BNTypeArchiveMergeSnapshots( + BNTypeArchive* archive, + const char* baseSnapshot, + const char* firstSnapshot, + const char* secondSnapshot, + const char* const* mergeConflictKeysIn, + const char* const* mergeConflictValuesIn, + size_t mergeConflictCountIn, + char*** mergeConflictsOut, + size_t* mergeConflictCountOut, + char** result, + bool (*progress)(void*, size_t, size_t), + void* context + ); + + BINARYNINJACOREAPI BNTypeArchive* BNBinaryViewAttachTypeArchive(BNBinaryView* view, const char* id, const char* path); + BINARYNINJACOREAPI bool BNBinaryViewDetachTypeArchive(BNBinaryView* view, const char* id); + BINARYNINJACOREAPI BNTypeArchive* BNBinaryViewGetTypeArchive(BNBinaryView* view, const char* id); + BINARYNINJACOREAPI size_t BNBinaryViewGetTypeArchives(BNBinaryView* view, char*** ids, char*** paths); + BINARYNINJACOREAPI char* BNBinaryViewGetTypeArchivePath(BNBinaryView* view, const char* id); + BINARYNINJACOREAPI size_t BNBinaryViewGetTypeArchiveTypeNameList(BNBinaryView* view, BNQualifiedName** names); + BINARYNINJACOREAPI size_t BNBinaryViewGetTypeArchiveTypeNames(BNBinaryView* view, BNQualifiedName* name, char*** archiveIds, char*** archiveTypeIds); + BINARYNINJACOREAPI size_t BNBinaryViewGetAssociatedTypeArchiveTypes(BNBinaryView* view, char*** typeIds, char*** archiveIds, char*** archiveTypeIds); + BINARYNINJACOREAPI size_t BNBinaryViewGetAssociatedTypesFromArchive(BNBinaryView* view, const char* archiveId, char*** typeIds, char*** archiveTypeIds); + BINARYNINJACOREAPI bool BNBinaryViewGetAssociatedTypeArchiveTypeTarget(BNBinaryView* view, const char* typeId, char** archiveId, char** archiveTypeId); + BINARYNINJACOREAPI bool BNBinaryViewGetAssociatedTypeArchiveTypeSource(BNBinaryView* view, const char* archiveId, const char* archiveTypeId, char** typeId); + BINARYNINJACOREAPI BNSyncStatus BNBinaryViewGetTypeArchiveSyncStatus(BNBinaryView* view, const char* typeId); + BINARYNINJACOREAPI bool BNBinaryViewDisassociateTypeArchiveType(BNBinaryView* view, const char* typeId); + BINARYNINJACOREAPI bool BNBinaryViewPullTypeArchiveTypes(BNBinaryView* view, const char* archiveId, const char* const* archiveTypeIds, size_t archiveTypeIdCount, char*** updatedArchiveTypeIds, char*** updatedAnalysisTypeIds, size_t* updatedTypeCount); + BINARYNINJACOREAPI bool BNBinaryViewPushTypeArchiveTypes(BNBinaryView* view, const char* archiveId, const char* const* typeIds, size_t typeIdCount, char*** updatedAnalysisTypeIds, char*** updatedArchiveTypeIds, size_t* updatedTypeCount); + #ifdef __cplusplus } #endif diff --git a/binaryview.cpp b/binaryview.cpp index aa275d4a..4a6adb92 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -462,6 +462,40 @@ void BinaryDataNotification::ExternalLocationRemovedCallback(void* ctxt, BNBinar } +void BinaryDataNotification::TypeArchiveAttachedCallback(void* ctxt, BNBinaryView* data, const char* id, const char* path) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + notify->OnTypeArchiveAttached(view, id, path); +} + + +void BinaryDataNotification::TypeArchiveDetachedCallback(void* ctxt, BNBinaryView* data, const char* id, const char* path) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + notify->OnTypeArchiveDetached(view, id, path); +} + + +void BinaryDataNotification::TypeArchiveConnectedCallback(void* ctxt, BNBinaryView* data, BNTypeArchive* archive) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<TypeArchive> apiArchive = new TypeArchive(BNNewTypeArchiveReference(archive)); + notify->OnTypeArchiveConnected(view, apiArchive); +} + + +void BinaryDataNotification::TypeArchiveDisconnectedCallback(void* ctxt, BNBinaryView* data, BNTypeArchive* archive) +{ + BinaryDataNotification* notify = (BinaryDataNotification*)ctxt; + Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); + Ref<TypeArchive> apiArchive = new TypeArchive(BNNewTypeArchiveReference(archive)); + notify->OnTypeArchiveDisconnected(view, apiArchive); +} + + BinaryDataNotification::BinaryDataNotification() { m_callbacks.context = this; @@ -510,6 +544,10 @@ BinaryDataNotification::BinaryDataNotification() m_callbacks.externalLocationAdded = ExternalLocationAddedCallback; m_callbacks.externalLocationUpdated = ExternalLocationUpdatedCallback; m_callbacks.externalLocationRemoved = ExternalLocationRemovedCallback; + m_callbacks.typeArchiveAttached = TypeArchiveAttachedCallback; + m_callbacks.typeArchiveDetached = TypeArchiveDetachedCallback; + m_callbacks.typeArchiveConnected = TypeArchiveConnectedCallback; + m_callbacks.typeArchiveDisconnected = TypeArchiveDisconnectedCallback; } @@ -561,6 +599,10 @@ BinaryDataNotification::BinaryDataNotification(NotificationTypes notifications) m_callbacks.externalLocationAdded = (notifications & NotificationType::ExternalLocationAdded) ? ExternalLocationAddedCallback : nullptr; m_callbacks.externalLocationUpdated = (notifications & NotificationType::ExternalLocationUpdated) ? ExternalLocationUpdatedCallback : nullptr; m_callbacks.externalLocationRemoved = (notifications & NotificationType::ExternalLocationRemoved) ? ExternalLocationRemovedCallback : nullptr; + m_callbacks.typeArchiveAttached = (notifications & NotificationType::TypeArchiveAttached) ? TypeArchiveAttachedCallback : nullptr; + m_callbacks.typeArchiveDetached = (notifications & NotificationType::TypeArchiveDetached) ? TypeArchiveDetachedCallback : nullptr; + m_callbacks.typeArchiveConnected = (notifications & NotificationType::TypeArchiveConnected) ? TypeArchiveConnectedCallback : nullptr; + m_callbacks.typeArchiveDisconnected = (notifications & NotificationType::TypeArchiveDisconnected) ? TypeArchiveDisconnectedCallback : nullptr; } @@ -4221,6 +4263,204 @@ std::optional<std::pair<Ref<TypeLibrary>, QualifiedName>> BinaryView::LookupImpo } +Ref<TypeArchive> BinaryView::AttachTypeArchive(const string& id, const string& path) +{ + BNTypeArchive* archive = BNBinaryViewAttachTypeArchive(m_object, id.c_str(), path.c_str()); + if (!archive) + return nullptr; + return new TypeArchive(archive); +} + + +void BinaryView::DetachTypeArchive(const string& id) +{ + BNBinaryViewDetachTypeArchive(m_object, id.c_str()); +} + + +Ref<TypeArchive> BinaryView::GetTypeArchive(const std::string& id) const +{ + BNTypeArchive* result = BNBinaryViewGetTypeArchive(m_object, id.c_str()); + if (!result) + return nullptr; + return new TypeArchive(result); +} + + +std::unordered_map<std::string, std::string> BinaryView::GetTypeArchives() const +{ + char** ids; + char** paths; + size_t count = BNBinaryViewGetTypeArchives(m_object, &ids, &paths); + + std::unordered_map<std::string, std::string> result; + for (size_t i = 0; i < count; i ++) + { + result.emplace(ids[i], paths[i]); + } + BNFreeStringList(ids, count); + BNFreeStringList(paths, count); + return result; +} + + +std::optional<std::string> BinaryView::GetTypeArchivePath(const std::string& id) const +{ + char* result = BNBinaryViewGetTypeArchivePath(m_object, id.c_str()); + if (!result) + return std::nullopt; + std::string cppResult = result; + BNFreeString(result); + return cppResult; +} + + +std::unordered_map<QualifiedName, std::map<std::string, std::string>> BinaryView::GetTypeArchiveTypeNames() const +{ + BNQualifiedName* names; + size_t nameCount = BNBinaryViewGetTypeArchiveTypeNameList(m_object, &names); + + std::unordered_map<QualifiedName, std::map<std::string, std::string>> result; + for (size_t i = 0; i < nameCount; i++) + { + char** archiveIds; + char** typeIds; + size_t idCount = BNBinaryViewGetTypeArchiveTypeNames(m_object, &names[i], &archiveIds, &typeIds); + + std::map<std::string, std::string> ids; + for (size_t j = 0; j < idCount; ++j) + { + ids.emplace(archiveIds[j], typeIds[j]); + } + BNFreeStringList(archiveIds, idCount); + BNFreeStringList(typeIds, idCount); + } + + BNFreeTypeNameList(names, nameCount); + return result; +} + + +std::unordered_map<std::string, std::pair<std::string, std::string>> BinaryView::GetAssociatedTypeArchiveTypes() const +{ + char** typeIds; + char** archiveIds; + char** archiveTypeIds; + size_t count = BNBinaryViewGetAssociatedTypeArchiveTypes(m_object, &typeIds, &archiveIds, &archiveTypeIds); + + std::unordered_map<std::string, std::pair<std::string, std::string>> result; + for (size_t i = 0; i < count; i++) + { + result.insert({typeIds[i], {archiveIds[i], archiveTypeIds[i]}}); + } + BNFreeStringList(typeIds, count); + BNFreeStringList(archiveIds, count); + BNFreeStringList(archiveTypeIds, count); + return result; +} + + +std::unordered_map<std::string, std::string> BinaryView::GetAssociatedTypesFromArchive(const std::string& archive) const +{ + char** typeIds; + char** archiveTypeIds; + size_t count = BNBinaryViewGetAssociatedTypesFromArchive(m_object, archive.c_str(), &typeIds, &archiveTypeIds); + + std::unordered_map<std::string, std::string> result; + for (size_t i = 0; i < count; i++) + { + result.insert({typeIds[i], archiveTypeIds[i]}); + } + BNFreeStringList(typeIds, count); + BNFreeStringList(archiveTypeIds, count); + return result; +} + + +std::optional<std::pair<std::string, std::string>> BinaryView::GetAssociatedTypeArchiveTypeTarget(const std::string& id) const +{ + char* archiveId; + char* archiveTypeId; + if (!BNBinaryViewGetAssociatedTypeArchiveTypeTarget(m_object, id.c_str(), &archiveId, &archiveTypeId)) + return std::nullopt; + std::pair<std::string, std::string> result = std::make_pair(archiveId, archiveTypeId); + BNFreeString(archiveId); + BNFreeString(archiveTypeId); + return result; +} + + +std::optional<std::string> BinaryView::GetAssociatedTypeArchiveTypeSource(const std::string& archiveId, const std::string& archiveTypeId) const +{ + char* typeId; + if (!BNBinaryViewGetAssociatedTypeArchiveTypeSource(m_object, archiveId.c_str(), archiveTypeId.c_str(), &typeId)) + return std::nullopt; + std::string result = typeId; + BNFreeString(typeId); + return result; +} + + +BNSyncStatus BinaryView::GetTypeArchiveSyncStatus(const std::string& typeId) const +{ + return BNBinaryViewGetTypeArchiveSyncStatus(m_object, typeId.c_str()); +} + + +bool BinaryView::DisassociateTypeArchiveType(const std::string& typeId) +{ + return BNBinaryViewDisassociateTypeArchiveType(m_object, typeId.c_str()); +} + + +bool BinaryView::PullTypeArchiveTypes(const std::string& archiveId, const std::unordered_set<std::string>& archiveTypeIds, std::unordered_map<std::string, std::string>& updatedTypes) +{ + std::vector<const char*> apiArchiveTypeIds; + for (const auto& archiveTypeId: archiveTypeIds) + { + apiArchiveTypeIds.push_back(archiveTypeId.c_str()); + } + + char** apiUpdatedArchiveTypeIds; + char** apiUpdatedAnalysisTypeIds; + size_t apiUpdatedTypeCount; + if (!BNBinaryViewPullTypeArchiveTypes(m_object, archiveId.c_str(), apiArchiveTypeIds.data(), apiArchiveTypeIds.size(), &apiUpdatedArchiveTypeIds, &apiUpdatedAnalysisTypeIds, &apiUpdatedTypeCount)) + return false; + updatedTypes.clear(); + for (size_t i = 0; i < apiUpdatedTypeCount; i++) + { + updatedTypes.insert({apiUpdatedArchiveTypeIds[i], apiUpdatedAnalysisTypeIds[i]}); + } + BNFreeStringList(apiUpdatedArchiveTypeIds, apiUpdatedTypeCount); + BNFreeStringList(apiUpdatedAnalysisTypeIds, apiUpdatedTypeCount); + return true; +} + + +bool BinaryView::PushTypeArchiveTypes(const std::string& archiveId, const std::unordered_set<std::string>& typeIds, std::unordered_map<std::string, std::string>& updatedTypes) +{ + std::vector<const char*> apiTypeIds; + for (const auto& typeId: typeIds) + { + apiTypeIds.push_back(typeId.c_str()); + } + + char** apiUpdatedAnalysisTypeIds; + char** apiUpdatedArchiveTypeIds; + size_t apiUpdatedTypeCount; + if (!BNBinaryViewPushTypeArchiveTypes(m_object, archiveId.c_str(), apiTypeIds.data(), apiTypeIds.size(), &apiUpdatedAnalysisTypeIds, &apiUpdatedArchiveTypeIds, &apiUpdatedTypeCount)) + return false; + updatedTypes.clear(); + for (size_t i = 0; i < apiUpdatedTypeCount; i++) + { + updatedTypes.insert({apiUpdatedAnalysisTypeIds[i], apiUpdatedArchiveTypeIds[i]}); + } + BNFreeStringList(apiUpdatedAnalysisTypeIds, apiUpdatedTypeCount); + BNFreeStringList(apiUpdatedArchiveTypeIds, apiUpdatedTypeCount); + return true; +} + + bool BinaryView::FindNextData(uint64_t start, const DataBuffer& data, uint64_t& result, BNFindFlag flags) { return BNFindNextData(m_object, start, data.GetBufferObject(), &result, flags); diff --git a/python/__init__.py b/python/__init__.py index 2b12d827..144d022a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -69,6 +69,7 @@ from .secretsprovider import * from .typeparser import * from .typeprinter import * from .component import * +from .typearchive import * from .typecontainer import * from .exceptions import * from .project import * diff --git a/python/binaryview.py b/python/binaryview.py index 96c93876..74f6c2ff 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -68,6 +68,7 @@ from . import highlevelil from . import debuginfo from . import flowgraph from . import project +from . import typearchive # The following are imported as such to allow the type checker disambiguate the module name # from properties and methods of the same name from . import workflow as _workflow @@ -174,6 +175,10 @@ class NotificationType(IntFlag): ComponentFunctionRemoved = 1 << 36 ComponentDataVariableAdded = 1 << 37 ComponentDataVariableRemoved = 1 << 38 + TypeArchiveAttached = 1 << 39 + TypeArchiveDetached = 1 << 40 + TypeArchiveConnected = 1 << 41 + TypeArchiveDisconnected = 1 << 42 BinaryDataUpdates = DataWritten | DataInserted | DataRemoved FunctionLifetime = FunctionAdded | FunctionRemoved FunctionUpdates = FunctionLifetime | FunctionUpdated @@ -191,6 +196,7 @@ class NotificationType(IntFlag): SectionLifetime = SectionAdded | SectionRemoved SectionUpdates = SectionLifetime | SectionUpdated ComponentUpdates = (ComponentAdded | ComponentRemoved | ComponentMoved | ComponentFunctionAdded | ComponentFunctionRemoved | ComponentDataVariableAdded | ComponentDataVariableRemoved) + TypeArchiveUpdates = (TypeArchiveAttached | TypeArchiveDetached | TypeArchiveConnected | TypeArchiveDisconnected) class BinaryDataNotification: @@ -386,7 +392,17 @@ class BinaryDataNotification: def component_data_var_removed(self, view: 'BinaryView', _component: component.Component, var: 'DataVariable'): pass + def type_archive_attached(self, view: 'BinaryView', id: str, path: str): + pass + + def type_archive_detached(self, view: 'BinaryView', id: str, path: str): + pass + def type_archive_connected(self, view: 'BinaryView', archive: 'typearchive.TypeArchive'): + pass + + def type_archive_disconnected(self, view: 'BinaryView', archive: 'typearchive.TypeArchive'): + pass class StringReference: @@ -635,6 +651,11 @@ class BinaryDataNotificationCallbacks: self._cb.componentFunctionRemoved = self._cb.componentFunctionRemoved.__class__(self._component_function_removed) self._cb.componentDataVariableAdded = self._cb.componentDataVariableAdded.__class__(self._component_data_variable_added) self._cb.componentDataVariableRemoved = self._cb.componentDataVariableRemoved.__class__(self._component_data_variable_removed) + + self._cb.typeArchiveAttached = self._cb.typeArchiveAttached.__class__(self._type_archive_attached) + self._cb.typeArchiveDetached = self._cb.typeArchiveDetached.__class__(self._type_archive_detached) + self._cb.typeArchiveConnected = self._cb.typeArchiveConnected.__class__(self._type_archive_connected) + self._cb.typeArchiveDisconnected = self._cb.typeArchiveDisconnected.__class__(self._type_archive_disconnected) else: if notify.notifications & NotificationType.NotificationBarrier: self._cb.notificationBarrier = self._cb.notificationBarrier.__class__(self._notification_barrier) @@ -715,6 +736,15 @@ class BinaryDataNotificationCallbacks: if notify.notifications & NotificationType.ComponentDataVariableRemoved: self._cb.componentDataVariableRemoved = self._cb.componentDataVariableRemoved.__class__(self._component_data_variable_removed) + if notify.notifications & NotificationType.TypeArchiveAttached: + self._cb.typeArchiveAttached = self._cb.typeArchiveAttached.__class__(self._type_archive_attached) + if notify.notifications & NotificationType.TypeArchiveDetached: + self._cb.typeArchiveDetached = self._cb.typeArchiveDetached.__class__(self._type_archive_detached) + if notify.notifications & NotificationType.TypeArchiveConnected: + self._cb.typeArchiveConnected = self._cb.typeArchiveConnected.__class__(self._type_archive_connected) + if notify.notifications & NotificationType.TypeArchiveDisconnected: + self._cb.typeArchiveDisconnected = self._cb.typeArchiveDisconnected.__class__(self._type_archive_disconnected) + def _register(self) -> None: core.BNRegisterDataNotification(self._view.handle, self._cb) @@ -1085,6 +1115,33 @@ class BinaryDataNotificationCallbacks: except: log_error(traceback.format_exc()) + def _type_archive_attached(self, ctxt, view: core.BNBinaryView, id: ctypes.c_char_p, path: ctypes.c_char_p): + try: + self._notify.type_archive_attached(self._view, core.pyNativeStr(id), core.pyNativeStr(path)) + except: + log_error(traceback.format_exc()) + + def _type_archive_detached(self, ctxt, view: core.BNBinaryView, id: ctypes.c_char_p, path: ctypes.c_char_p): + try: + self._notify.type_archive_detached(self._view, core.pyNativeStr(id), core.pyNativeStr(path)) + except: + log_error(traceback.format_exc()) + + def _type_archive_connected(self, ctxt, view: core.BNBinaryView, archive: core.BNTypeArchive): + try: + py_archive = typearchive.TypeArchive(handle=core.BNNewTypeArchiveReference(archive)) + self._notify.type_archive_connected(self._view, py_archive) + except: + log_error(traceback.format_exc()) + + def _type_archive_disconnected(self, ctxt, view: core.BNBinaryView, archive: core.BNTypeArchive): + try: + py_archive = typearchive.TypeArchive(handle=core.BNNewTypeArchiveReference(archive)) + self._notify.type_archive_disconnected(self._view, py_archive) + except: + log_error(traceback.format_exc()) + + @property def view(self) -> 'BinaryView': return self._view @@ -2842,6 +2899,31 @@ class BinaryView: core.BNFreeTypeLibraryList(libraries, count.value) @property + def attached_type_archives(self) -> Mapping['str', 'str']: + """All attached type archive ids and paths (read-only)""" + ids = ctypes.POINTER(ctypes.c_char_p)() + paths = ctypes.POINTER(ctypes.c_char_p)() + count = core.BNBinaryViewGetTypeArchives(self.handle, ids, paths) + result = {} + try: + for i in range(0, count): + result[core.pyNativeStr(ids[i])] = core.pyNativeStr(paths[i]) + return result + finally: + core.BNFreeStringList(ids, count) + core.BNFreeStringList(paths, count) + + @property + def connected_type_archives(self) -> List['typearchive.TypeArchive']: + """All connected type archive objects (read-only)""" + result = [] + for (id, path) in self.attached_type_archives.items(): + archive = self.get_type_archive(id) + if archive is not None: + result.append(archive) + return result + + @property def segments(self) -> List['Segment']: """List of segments (read-only)""" count = ctypes.c_ulonglong(0) @@ -7707,6 +7789,387 @@ class BinaryView: core.BNFreeQualifiedName(result_name) return lib, name + def attach_type_archive(self, archive: 'typearchive.TypeArchive'): + """ + Attach a given type archive to the owned analysis and try to connect to it. + Names from that archive will be cached in the mapping but no types will actually be associated by calling this. + :param archive: New archive + """ + attached = self.attach_type_archive_by_id(archive.id, archive.path) + assert attached == archive + + def attach_type_archive_by_id(self, id: str, path: str) -> Optional['typearchive.TypeArchive']: + """ + Attach a type archive to the owned analysis and try to connect to it. + If attaching was successful, names from that archive will be cached in the mapping, + but no types will actually be associated by calling this. + + The behavior of this function is rather complicated, in an attempt to enable + control of both attaching and connecting Type Archives. + + If there was a previously connected Type Archive whose id matches `id`, nothing + will happen and it will simply be returned. + If there was no previously connected Type Archive whose id matches `id`, and the + file at `path` contains a Type Archive whose id matches `id`, it will be + attached and connected. + + If the file at `path` does not exist, nothing will happen and None will be returned. + If the file at `path` exists but does not contain a Type Archive whose id matches `id`, + nothing will happen and None will be returned. + If there was a previously attached but disconnected Type Archive whose id matches `id`, + and the file at `path` contains a Type Archive whose id matches `id`, the + previously attached Type Archive will have its saved path updated to point + to `path`. The Type Archive at `path` will be connected and returned. + + :param id: Id of Type Archive to attach + :param path: Path to file of Type Archive to attach + :return: Attached archive object, if it could be connected. + """ + archive = core.BNBinaryViewAttachTypeArchive(self.handle, id, path) + if not archive: + return None + return typearchive.TypeArchive(handle=archive) + + def detach_type_archive(self, archive: 'typearchive.TypeArchive'): + """ + Detach from a type archive, breaking all associations to types with the archive + :param archive: Type archive to detach + """ + self.detach_type_archive_by_id(archive.id) + + def detach_type_archive_by_id(self, id: str): + """ + Detach from a type archive, breaking all associations to types with the archive + :param id: Id of archive to detach + """ + if not core.BNBinaryViewDetachTypeArchive(self.handle, id): + raise RuntimeError("BNBinaryViewDetachTypeArchive") + + def get_type_archive(self, id: str) -> Optional['typearchive.TypeArchive']: + """ + Look up a connected archive by its id + :param id: Id of archive + :return: Archive, if one exists with that id. Otherwise None + """ + result = core.BNBinaryViewGetTypeArchive(self.handle, id) + if result is None: + return None + return typearchive.TypeArchive(result) + + def get_type_archive_path(self, id: str) -> Optional[str]: + """ + Look up the path for an attached (but not necessarily connected) type archive by its id + :param id: Id of archive + :return: Archive path, if it is attached. Otherwise None. + """ + result = core.BNBinaryViewGetTypeArchivePath(self.handle, id) + if result is None: + return None + return result + + @property + def type_archive_type_names(self) -> Mapping['_types.QualifiedName', List[Tuple['typearchive.TypeArchive', str]]]: + """ + Get a list of all available type names in all connected archives, and their archive/type id pair + :return: name <-> [(archive, archive type id)] for all type names + """ + names = ctypes.POINTER(core.BNQualifiedName)() + name_count = core.BNBinaryViewGetTypeArchiveTypeNameList(self.handle, names) + + result = {} + try: + for i in range(0, name_count): + name = _types.QualifiedName._from_core_struct(names[i]) + result[name] = self.get_type_archives_for_type_name(name) + return result + finally: + core.BNFreeTypeNameList(names, name_count) + + def get_type_archives_for_type_name(self, name: '_types.QualifiedNameType') -> List[Tuple['typearchive.TypeArchive', str]]: + """ + Get a list of all connected type archives that have a given type name + :return: (archive, archive type id) for all archives + """ + name = _types.QualifiedName(name) + archive_ids = ctypes.POINTER(ctypes.c_char_p)() + type_ids = ctypes.POINTER(ctypes.c_char_p)() + id_count = core.BNBinaryViewGetTypeArchiveTypeNames(self.handle, name._to_core_struct(), archive_ids, type_ids) + ids = [] + + type_archives = self.connected_type_archives + type_archives_by_id = {} + for archive in type_archives: + type_archives_by_id[archive.id] = archive + try: + for j in range(0, id_count): + ids.append((type_archives_by_id[core.pyNativeStr(archive_ids[j])], core.pyNativeStr(type_ids[j]))) + return ids + finally: + core.BNFreeStringList(archive_ids, id_count) + core.BNFreeStringList(type_ids, id_count) + + @property + def associated_type_archive_types(self) -> Mapping['_types.QualifiedName', Tuple[Optional['typearchive.TypeArchive'], str]]: + """ + Get a list of all types in the analysis that are associated with attached type archives + :return: Map of all analysis types to their corresponding archive / id. + If a type is associated with a disconnected type archive, the archive will be None. + """ + result = {} + + type_archives = self.attached_type_archives + type_archives_by_id = {} + for (archive_id, _) in type_archives.items(): + type_archives_by_id[archive_id] = self.get_type_archive(archive_id) + + for type_id, (archive_id, archive_type_id) in self.associated_type_archive_type_ids.items(): + name = self.get_type_name_by_id(type_id) + if name is None: + continue + result[name] = (type_archives_by_id.get(archive_id, None), archive_type_id) + return result + + @property + def associated_type_archive_type_ids(self) -> Mapping[str, Tuple[str, str]]: + """ + Get a list of all types in the analysis that are associated with type archives + :return: Map of all analysis types to their corresponding archive / id + """ + + type_ids = ctypes.POINTER(ctypes.c_char_p)() + archive_ids = ctypes.POINTER(ctypes.c_char_p)() + archive_type_ids = ctypes.POINTER(ctypes.c_char_p)() + count = core.BNBinaryViewGetAssociatedTypeArchiveTypes(self.handle, type_ids, archive_ids, archive_type_ids) + + result = {} + try: + for i in range(0, count): + type_id = core.pyNativeStr(type_ids[i]) + archive_id = core.pyNativeStr(archive_ids[i]) + archive_type_id = core.pyNativeStr(archive_type_ids[i]) + result[type_id] = (archive_id, archive_type_id) + return result + finally: + core.BNFreeStringList(type_ids, count) + core.BNFreeStringList(archive_ids, count) + core.BNFreeStringList(archive_type_ids, count) + + def get_associated_types_from_archive(self, archive: 'typearchive.TypeArchive') -> Mapping['_types.QualifiedName', str]: + """ + Get a list of all types in the analysis that are associated with a specific type archive + :return: Map of all analysis types to their corresponding archive id + """ + result = {} + + for type_id, archive_type_id in self.get_associated_types_from_archive_by_id(archive.id).items(): + name = self.get_type_name_by_id(type_id) + if name is None: + continue + result[name] = archive_type_id + return result + + def get_associated_types_from_archive_by_id(self, archive_id: str) -> Mapping[str, str]: + """ + Get a list of all types in the analysis that are associated with a specific type archive + :return: Map of all analysis types to their corresponding archive id + """ + + type_ids = ctypes.POINTER(ctypes.c_char_p)() + archive_type_ids = ctypes.POINTER(ctypes.c_char_p)() + count = core.BNBinaryViewGetAssociatedTypesFromArchive(self.handle, archive_id, type_ids, archive_type_ids) + + result = {} + try: + for i in range(0, count): + type_id = core.pyNativeStr(type_ids[i]) + archive_type_id = core.pyNativeStr(archive_type_ids[i]) + result[type_id] = archive_type_id + return result + finally: + core.BNFreeStringList(type_ids, count) + core.BNFreeStringList(archive_type_ids, count) + + def get_associated_type_archive_type_target(self, name: '_types.QualifiedNameType') -> Optional[Tuple[Optional['typearchive.TypeArchive'], str]]: + """ + Determine the target archive / type id of a given analysis type + :param name: Analysis type + :return: (archive, archive type id) if the type is associated. None otherwise. + """ + type_id = self.get_type_id(name) + if type_id == '': + return None + result = self.get_associated_type_archive_type_target_by_id(type_id) + if result is None: + return None + archive_id, type_id = result + archive = self.get_type_archive(archive_id) + return archive, type_id + + def get_associated_type_archive_type_target_by_id(self, type_id: str) -> Optional[Tuple[str, str]]: + """ + Determine the target archive / type id of a given analysis type + :param type_id: Analysis type id + :return: (archive id, archive type id) if the type is associated. None otherwise. + """ + archive_id = ctypes.c_char_p() + archive_type_id = ctypes.c_char_p() + if not core.BNBinaryViewGetAssociatedTypeArchiveTypeTarget(self.handle, type_id, archive_id, archive_type_id): + return None + result = (core.pyNativeStr(archive_id.value), core.pyNativeStr(archive_type_id.value)) + core.free_string(archive_id) + core.free_string(archive_type_id) + return result + + def get_associated_type_archive_type_source(self, archive: 'typearchive.TypeArchive', archive_type: '_types.QualifiedNameType') -> Optional['_types.QualifiedName']: + """ + Determine the local source type name for a given archive type + :param archive: Target type archive + :param archive_type: Name of target archive type + :return: Name of source analysis type, if this type is associated. None otherwise. + """ + archive_type_id = archive.get_type_id(archive_type) + if archive_type_id is None: + return None + result = self.get_associated_type_archive_type_source_by_id(archive.id, archive_type_id) + if result is None: + return None + return self.get_type_name_by_id(result) + + def get_associated_type_archive_type_source_by_id(self, archive_id: str, archive_type_id: str) -> Optional[str]: + """ + Determine the local source type id for a given archive type + :param archive_id: Id of target type archive + :param archive_type_id: Id of target archive type + :return: Id of source analysis type, if this type is associated. None otherwise. + """ + type_id = ctypes.c_char_p() + if not core.BNBinaryViewGetAssociatedTypeArchiveTypeSource(self.handle, archive_id, archive_type_id, type_id): + return None + result = core.pyNativeStr(type_id.value) + core.free_string(type_id) + return result + + def disassociate_type_archive_type(self, type: '_types.QualifiedNameType') -> bool: + """ + Disassociate an associated type, so that it will no longer receive updates from its connected type archive + :param type: Name of type in analysis + :return: True if successful + """ + type_id = self.get_type_id(type) + if type_id == '': + return False + return self.disassociate_type_archive_type_by_id(type_id) + + def disassociate_type_archive_type_by_id(self, type_id: str) -> bool: + """ + Disassociate an associated type id, so that it will no longer receive updates from its connected type archive + :param type_id: Id of type in analysis + :return: True if successful + """ + return core.BNBinaryViewDisassociateTypeArchiveType(self.handle, type_id) + + def pull_types_from_archive(self, archive: 'typearchive.TypeArchive', names: List['_types.QualifiedNameType']) \ + -> Optional[Mapping['_types.QualifiedName', Tuple['_types.QualifiedName', '_types.Type']]]: + """ + Pull types from a type archive, updating them and any dependencies + :param archive: Target type archive + :param names: Names of desired types in type archive + :return: { name: (name, type) } Mapping from archive name to (analysis name, definition), None on error + """ + archive_type_ids = [] + for name in names: + archive_type_id = archive.get_type_id(name) + if archive_type_id is None: + return None + archive_type_ids.append(archive_type_id) + result = self.pull_types_from_archive_by_id(archive.id, archive_type_ids) + if result is None: + return None + + results = {} + for (archive_type_id, analysis_type_id) in result.items(): + results[archive.get_type_name_by_id(archive_type_id)] = (self.get_type_name_by_id(analysis_type_id), self.get_type_by_id(analysis_type_id)) + + return results + + def pull_types_from_archive_by_id(self, archive_id: str, archive_type_ids: List[str]) \ + -> Optional[Mapping[str, str]]: + """ + Pull types from a type archive by id, updating them and any dependencies + :param archive_id: Target type archive id + :param archive_type_ids: Ids of desired types in type archive + :return: { id: id } Mapping from archive type id to analysis type id, None on error + """ + api_ids = (ctypes.c_char_p * len(archive_type_ids))() + for i, id in enumerate(archive_type_ids): + api_ids[i] = core.cstr(id) + + updated_archive_type_strs = ctypes.POINTER(ctypes.c_char_p)() + updated_analysis_type_strs = ctypes.POINTER(ctypes.c_char_p)() + updated_type_count = ctypes.c_size_t(0) + try: + if not core.BNBinaryViewPullTypeArchiveTypes(self.handle, archive_id, api_ids, len(archive_type_ids), updated_archive_type_strs, updated_analysis_type_strs, updated_type_count): + return None + + results = {} + for i in range(0, updated_type_count.value): + results[core.pyNativeStr(updated_archive_type_strs[i])] = core.pyNativeStr(updated_analysis_type_strs[i]) + return results + finally: + core.BNFreeStringList(updated_archive_type_strs, updated_type_count.value) + core.BNFreeStringList(updated_analysis_type_strs, updated_type_count.value) + + def push_types_to_archive(self, archive: 'typearchive.TypeArchive', names: List['_types.QualifiedNameType']) \ + -> Optional[Mapping['_types.QualifiedName', Tuple['_types.QualifiedName', '_types.Type']]]: + """ + Push a collection of types, and all their dependencies, into a type archive + :param archive: Target type archive + :param names: Names of types in analysis + :return: { name: (name, type) } Mapping from analysis name to (archive name, definition), None on error + """ + analysis_type_ids = [] + for name in names: + analysis_type_id = self.get_type_id(name) + if analysis_type_id is None: + return None + analysis_type_ids.append(analysis_type_id) + result = self.push_types_to_archive_by_id(archive.id, analysis_type_ids) + if result is None: + return None + + results = {} + for (analysis_type_id, archive_type_id) in result.items(): + results[self.get_type_name_by_id(analysis_type_id)] = (archive.get_type_name_by_id(archive_type_id), archive.get_type_by_id(archive_type_id)) + + return results + + def push_types_to_archive_by_id(self, archive_id: str, type_ids: List[str]) \ + -> Optional[Mapping[str, str]]: + """ + Push a collection of types, and all their dependencies, into a type archive + :param archive_id: Id of target type archive + :param type_ids: Ids of types in analysis + :return: True if successful + """ + api_ids = (ctypes.c_char_p * len(type_ids))() + for i, id in enumerate(type_ids): + api_ids[i] = core.cstr(id) + + updated_analysis_type_strs = ctypes.POINTER(ctypes.c_char_p)() + updated_archive_type_strs = ctypes.POINTER(ctypes.c_char_p)() + updated_type_count = ctypes.c_size_t(0) + try: + if not core.BNBinaryViewPushTypeArchiveTypes(self.handle, archive_id, api_ids, len(type_ids), updated_analysis_type_strs, updated_archive_type_strs, updated_type_count): + return None + + results = {} + for i in range(0, updated_type_count.value): + results[core.pyNativeStr(updated_analysis_type_strs[i])] = core.pyNativeStr(updated_archive_type_strs[i]) + return results + finally: + core.BNFreeStringList(updated_analysis_type_strs, updated_type_count.value) + core.BNFreeStringList(updated_archive_type_strs, updated_type_count.value) + def register_platform_types(self, platform: '_platform.Platform') -> None: """ ``register_platform_types`` ensures that the platform-specific types for a :py:class:`Platform` are available diff --git a/python/typearchive.py b/python/typearchive.py new file mode 100644 index 00000000..c4e94a5e --- /dev/null +++ b/python/typearchive.py @@ -0,0 +1,758 @@ +# Copyright (c) 2015-2023 Vector 35 Inc +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes +import traceback +from typing import Optional, List, Dict, Union, Tuple + +# Binary Ninja components +import binaryninja +from . import _binaryninjacore as core +from . import types as ty_ +from . import log +from . import metadata +from . import databuffer +from . import platform +from . import architecture +from . import binaryview + + +class TypeArchive: + def __init__(self, handle: core.BNTypeArchiveHandle): + binaryninja._init_plugins() + self.handle: core.BNTypeArchiveHandle = core.handle_of_type(handle, core.BNTypeArchive) + self._notifications = {} + + def __hash__(self): + return hash(ctypes.addressof(self.handle.contents)) + + def __del__(self): + if core is not None: + core.BNFreeTypeArchiveReference(self.handle) + + def __repr__(self): + return f"<type archive '{self.path}'>" + + def __eq__(self, other): + if not isinstance(other, TypeArchive): + return False + return self.id == other.id + + @staticmethod + def open(path: str) -> Optional['TypeArchive']: + """ + Open the Type Archive at the given path, if it exists. + :param path: Path to Type Archive file + :return: Type Archive, or None if it could not be loaded. + """ + handle = core.BNOpenTypeArchive(path) + if handle is None: + return None + return TypeArchive(handle=handle) + + @staticmethod + def create(path: str, platform: 'platform.Platform') -> Optional['TypeArchive']: + """ + Create a Type Archive at the given path. + :param path: Path to Type Archive file + :param platform: Relevant platform for types in the archive + :return: Type Archive, or None if it could not be created. + """ + handle = core.BNCreateTypeArchive(path, platform.handle) + if handle is None: + return None + return TypeArchive(handle=handle) + + @staticmethod + def lookup_by_id(id: str) -> Optional['TypeArchive']: + """ + Get a reference to the Type Archive with the known id, if one exists. + :param id: Type Archive id + :return: Type archive, or None if it could not be found. + """ + handle = core.BNLookupTypeArchiveById(id) + if handle is None: + return None + return TypeArchive(handle=handle) + + @property + def path(self) -> Optional[str]: + """ + Get the path to the Type Archive's file + :return: File path + """ + return core.BNGetTypeArchivePath(self.handle) + + @property + def id(self) -> Optional[str]: + """ + Get the guid for a Type Archive + :return: Guid string + """ + return core.BNGetTypeArchiveId(self.handle) + + @property + def platform(self) -> 'platform.Platform': + """ + Get the associated Platform for a Type Archive + :return: Platform object + """ + handle = core.BNGetTypeArchivePlatform(self.handle) + assert handle is not None + return platform.Platform(handle=handle) + + @property + def current_snapshot_id(self) -> str: + """ + Get the id of the current snapshot in the type archive + :return: Snapshot id + """ + result = core.BNGetTypeArchiveCurrentSnapshotId(self.handle) + if result is None: + raise RuntimeError("BNGetTypeArchiveCurrentSnapshotId") + return result + + @current_snapshot_id.setter + def current_snapshot_id(self, value: str): + """ + Revert the type archive's current snapshot to the given snapshot + :param value: Snapshot id + """ + core.BNSetTypeArchiveCurrentSnapshot(self.handle, value) + + @property + def all_snapshot_ids(self) -> List[str]: + """ + Get a list of every snapshot's id + :return: All ids (including the empty first snapshot) + """ + count = ctypes.c_ulonglong(0) + ids = core.BNGetTypeArchiveAllSnapshotIds(self.handle, count) + if ids is None: + raise RuntimeError("BNGetTypeArchiveAllSnapshotIds") + result = [] + try: + for i in range(0, count.value): + result.append(core.pyNativeStr(ids[i])) + return result + finally: + core.BNFreeStringList(ids, count.value) + + def get_snapshot_parent_ids(self, snapshot: str) -> Optional[List[str]]: + """ + Get the ids of the parents to the given snapshot + :param snapshot: Child snapshot id + :return: Parent snapshot ids, or empty list if the snapshot is a root + """ + count = ctypes.c_size_t(0) + ids = core.BNGetTypeArchiveSnapshotParentIds(self.handle, snapshot, count) + if ids is None: + raise RuntimeError("BNGetTypeArchiveSnapshotParentIds") + result = [] + try: + for i in range(0, count.value): + result.append(core.pyNativeStr(ids[i])) + return result + finally: + core.BNFreeStringList(ids, count.value) + + def get_snapshot_child_ids(self, snapshot: str) -> Optional[List[str]]: + """ + Get the ids of the children to the given snapshot + :param snapshot: Parent snapshot id + :return: Child snapshot ids, or empty list if the snapshot is a root + """ + count = ctypes.c_size_t(0) + ids = core.BNGetTypeArchiveSnapshotChildIds(self.handle, snapshot, count) + if ids is None: + raise RuntimeError("BNGetTypeArchiveSnapshotChildIds") + result = [] + try: + for i in range(0, count.value): + result.append(core.pyNativeStr(ids[i])) + return result + finally: + core.BNFreeStringList(ids, count.value) + + def add_type(self, name: 'ty_.QualifiedNameType', type: 'ty_.Type') -> None: + """ + Add named types to the type archive. Type must have all dependant named types added + prior to being added, or this function will fail. + If the type already exists, it will be overwritten. + :param name: Name of new type + :param type: Definition of new type + """ + self.add_types([(name, type)]) + + def add_types(self, new_types: List[Tuple['ty_.QualifiedNameType', 'ty_.Type']]) -> None: + """ + Add named types to the type archive. Types must have all dependant named + types prior to being added, or this function will fail. + Types already existing with any added names will be overwritten. + :param new_types: Names and definitions of new types + """ + api_types = (core.BNQualifiedNameAndType * len(new_types))() + i = 0 + for (name, type) in new_types: + if not isinstance(name, ty_.QualifiedName): + name = ty_.QualifiedName(name) + type = type.immutable_copy() + if not isinstance(type, ty_.Type): + raise ValueError("parameter type must be a Type") + api_types[i].name = name._to_core_struct() + api_types[i].type = type.handle + i += 1 + + if not core.BNAddTypeArchiveTypes(self.handle, api_types, len(new_types)): + raise RuntimeError("BNAddTypeArchiveTypes") + + def rename_type(self, old_name: 'ty_.QualifiedNameType', new_name: 'ty_.QualifiedNameType') -> None: + """ + Change the name of an existing type in the type archive. + :param old_name: Old type name in archive + :param new_name: New type name + """ + id = self.get_type_id(old_name) + return self.rename_type_by_id(id, new_name) + + def rename_type_by_id(self, id: str, new_name: 'ty_.QualifiedNameType') -> None: + """ + Change the name of an existing type in the type archive. + :param id: Old id of type in archive + :param new_name: New type name + """ + if not isinstance(new_name, ty_.QualifiedName): + new_name = ty_.QualifiedName(new_name) + if not core.BNRenameTypeArchiveType(self.handle, id, new_name._to_core_struct()): + raise RuntimeError("BNRenameTypeArchiveType") + + def delete_type(self, name: 'ty_.QualifiedNameType') -> None: + """ + Delete an existing type in the type archive. + :param name: Type name + """ + id = self.get_type_id(name) + if id is None: + raise RuntimeError(f"Unknown type {name}") + self.delete_type_by_id(id) + + def delete_type_by_id(self, id: str) -> None: + """ + Delete an existing type in the type archive. + :param id: Type id + """ + if not core.BNDeleteTypeArchiveType(self.handle, id): + raise RuntimeError("BNDeleteTypeArchiveType") + + def get_type_by_name(self, name: 'ty_.QualifiedNameType', snapshot: Optional[str] = None) -> Optional[ty_.Type]: + """ + Retrieve a stored type in the archive + :param name: Type name + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: Type, if it exists. Otherwise None + """ + if snapshot is None: + snapshot = self.current_snapshot_id + if not isinstance(name, ty_.QualifiedName): + name = ty_.QualifiedName(name) + t = core.BNGetTypeArchiveTypeByName(self.handle, name._to_core_struct(), snapshot) + if t is None: + return None + return ty_.Type.create(t) + + def get_type_by_id(self, id: str, snapshot: Optional[str] = None) -> Optional[ty_.Type]: + """ + Retrieve a stored type in the archive by id + :param id: Type id + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: Type, if it exists. Otherwise None + """ + if snapshot is None: + snapshot = self.current_snapshot_id + assert id is not None + t = core.BNGetTypeArchiveTypeById(self.handle, id, snapshot) + if t is None: + return None + return ty_.Type.create(t) + + def get_type_name_by_id(self, id: str, snapshot: Optional[str] = None) -> Optional['ty_.QualifiedName']: + """ + Retrieve a type's name by its id + :param id: Type id + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: Type name, if it exists. Otherwise None + """ + if snapshot is None: + snapshot = self.current_snapshot_id + assert id is not None + name = core.BNGetTypeArchiveTypeName(self.handle, id, snapshot) + if name is None: + return None + qname = ty_.QualifiedName._from_core_struct(name) + if len(qname.name) == 0: + return None + return qname + + def get_type_id(self, name: 'ty_.QualifiedNameType', snapshot: Optional[str] = None) -> Optional[str]: + """ + Retrieve a type's id by its name + :param name: Type name + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: Type id, if it exists. Otherwise None + """ + if snapshot is None: + snapshot = self.current_snapshot_id + if not isinstance(name, ty_.QualifiedName): + name = ty_.QualifiedName(name) + t = core.BNGetTypeArchiveTypeId(self.handle, name._to_core_struct(), snapshot) + if t is None: + return None + if t == "": + return None + return t + + @property + def types(self) -> Dict[ty_.QualifiedName, ty_.Type]: + """ + Retrieve all stored types in the archive at the current snapshot + :return: Map of all types, by name + """ + return self.get_types() + + @property + def types_and_ids(self) -> Dict[str, Tuple[ty_.QualifiedName, ty_.Type]]: + """ + Retrieve all stored types in the archive at the current snapshot + :return: Map of type id to type name and definition + """ + return self.get_types_and_ids() + + def get_types(self, snapshot: Optional[str] = None) -> Dict[ty_.QualifiedName, ty_.Type]: + """ + Retrieve all stored types in the archive at a snapshot + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: Map of all types, by name + """ + result = {} + for id, (name, type) in self.get_types_and_ids(snapshot).items(): + result[name] = type + return result + + def get_types_and_ids(self, snapshot: Optional[str] = None) -> Dict[str, Tuple[ty_.QualifiedName, ty_.Type]]: + """ + Retrieve all stored types in the archive at a snapshot + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: Map of type id to type name and definition + """ + if snapshot is None: + snapshot = self.current_snapshot_id + count = ctypes.c_ulonglong(0) + result = {} + named_types = core.BNGetTypeArchiveTypes(self.handle, snapshot, count) + assert named_types is not None, "core.BNGetTypeArchiveTypes returned None" + try: + for i in range(0, count.value): + name = ty_.QualifiedName._from_core_struct(named_types[i].name) + id = core.pyNativeStr(named_types[i].id) + result[id] = (name, ty_.Type.create(core.BNNewTypeReference(named_types[i].type))) + return result + finally: + core.BNFreeTypeIdList(named_types, count.value) + + @property + def type_ids(self) -> List[str]: + """ + Get a list of all types' ids in the archive at the current snapshot + :return: All type ids + """ + return self.get_type_ids() + + def get_type_ids(self, snapshot: Optional[str] = None) -> List[str]: + """ + Get a list of all types' ids in the archive at a snapshot + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: All type ids + """ + if snapshot is None: + snapshot = self.current_snapshot_id + count = ctypes.c_ulonglong(0) + result = [] + ids = core.BNGetTypeArchiveTypeIds(self.handle, snapshot, count) + assert ids is not None, "core.BNGetTypeArchiveTypeIds returned None" + try: + for i in range(count.value): + result.append(core.pyNativeStr(ids[i])) + return result + finally: + core.BNFreeStringList(ids, count.value) + + @property + def type_names(self) -> List['ty_.QualifiedName']: + """ + Get a list of all types' names in the archive at the current snapshot + :return: All type names + """ + return self.get_type_names() + + def get_type_names(self, snapshot: Optional[str] = None) -> List['ty_.QualifiedName']: + """ + Get a list of all types' names in the archive at a snapshot + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: All type names + """ + if snapshot is None: + snapshot = self.current_snapshot_id + count = ctypes.c_ulonglong(0) + result = [] + names = core.BNGetTypeArchiveTypeNames(self.handle, snapshot, count) + assert names is not None, "core.BNGetTypeArchiveTypeNames returned None" + try: + for i in range(count.value): + result.append(ty_.QualifiedName._from_core_struct(names[i])) + return result + finally: + core.BNFreeQualifiedNameArray(names, count.value) + + @property + def type_names_and_ids(self) -> Dict[str, 'ty_.QualifiedName']: + """ + Get a list of all types' names and ids in the archive at the current snapshot + :return: Mapping of all type ids to names + """ + return self.get_type_names_and_ids() + + def get_type_names_and_ids(self, snapshot: Optional[str] = None) -> Dict[str, 'ty_.QualifiedName']: + """ + Get a list of all types' names and ids in the archive at a current snapshot + :param snapshot: Snapshot id to search for types, or None to search the latest snapshot + :return: Mapping of all type ids to names + """ + if snapshot is None: + snapshot = self.current_snapshot_id + names = ctypes.POINTER(core.BNQualifiedName)() + ids = ctypes.POINTER(ctypes.c_char_p)() + count = ctypes.c_ulonglong(0) + result = {} + if not core.BNGetTypeArchiveTypeNamesAndIds(self.handle, snapshot, names, ids, count): + raise RuntimeError("core.BNGetTypeArchiveTypeNamesAndIds returned False") + try: + for i in range(count.value): + id = core.pyNativeStr(ids[i]) + name = ty_.QualifiedName._from_core_struct(names[i]) + result[id] = name + return result + finally: + core.BNFreeQualifiedNameArray(names, count.value) + + def get_outgoing_direct_references(self, id: str, snapshot: Optional[str] = None) -> List[str]: + """ + Get all types a given type references directly + :param id: Source type id + :param snapshot: Snapshot id to search for types, or empty string to search the latest snapshot + :return: Target type ids + """ + if snapshot is None: + snapshot = self.current_snapshot_id + assert id is not None + count = ctypes.c_size_t(0) + ids = core.BNGetTypeArchiveOutgoingDirectTypeReferences(self.handle, id, snapshot, count) + if ids is None: + raise RuntimeError("BNGetTypeArchiveOutgoingDirectTypeReferences") + result = [] + try: + for i in range(0, count.value): + result.append(core.pyNativeStr(ids[i])) + return result + finally: + core.BNFreeStringList(ids, count.value) + + def get_outgoing_recursive_references(self, id: str, snapshot: Optional[str] = None) -> List[str]: + """ + Get all types a given type references, and any types that the referenced types reference + :param id: Source type id + :param snapshot: Snapshot id to search for types, or empty string to search the latest snapshot + :return: Target type ids + """ + if snapshot is None: + snapshot = self.current_snapshot_id + assert id is not None + count = ctypes.c_size_t(0) + ids = core.BNGetTypeArchiveOutgoingRecursiveTypeReferences(self.handle, id, snapshot, count) + if ids is None: + raise RuntimeError("BNGetTypeArchiveOutgoingRecursiveTypeReferences") + result = [] + try: + for i in range(0, count.value): + result.append(core.pyNativeStr(ids[i])) + return result + finally: + core.BNFreeStringList(ids, count.value) + + def get_incoming_direct_references(self, id: str, snapshot: Optional[str] = None) -> List[str]: + """ + Get all types that reference a given type + :param id: Target type id + :param snapshot: Snapshot id to search for types, or empty string to search the latest snapshot + :return: Source type ids + """ + if snapshot is None: + snapshot = self.current_snapshot_id + assert id is not None + count = ctypes.c_size_t(0) + ids = core.BNGetTypeArchiveIncomingDirectTypeReferences(self.handle, id, snapshot, count) + if ids is None: + raise RuntimeError("BNGetTypeArchiveIncomingDirectTypeReferences") + result = [] + try: + for i in range(0, count.value): + result.append(core.pyNativeStr(ids[i])) + return result + finally: + core.BNFreeStringList(ids, count.value) + + def get_incoming_recursive_references(self, id: str, snapshot: Optional[str] = None) -> List[str]: + """ + Get all types that reference a given type, and all types that reference them, recursively + :param id: Target type id + :param snapshot: Snapshot id to search for types, or empty string to search the latest snapshot + :return: Source type ids + """ + if snapshot is None: + snapshot = self.current_snapshot_id + assert id is not None + count = ctypes.c_size_t(0) + ids = core.BNGetTypeArchiveIncomingRecursiveTypeReferences(self.handle, id, snapshot, count) + if ids is None: + raise RuntimeError("BNGetTypeArchiveIncomingRecursiveTypeReferences") + result = [] + try: + for i in range(0, count.value): + result.append(core.pyNativeStr(ids[i])) + return result + finally: + core.BNFreeStringList(ids, count.value) + + def query_metadata(self, key: str) -> Optional['metadata.MetadataValueType']: + """ + Look up a metadata entry in the archive + :param string key: key to query + :rtype: Metadata associated with the key, if it exists. Otherwise, None + :Example: + + >>> ta: TypeArchive + >>> ta.store_metadata("ordinals", {"9": "htons"}) + >>> ta.query_metadata("ordinals")["9"] + "htons" + """ + md_handle = core.BNTypeArchiveQueryMetadata(self.handle, key) + if md_handle is None: + return None + return metadata.Metadata(handle=md_handle).value + + def store_metadata(self, key: str, md: 'metadata.MetadataValueType') -> None: + """ + Store a key/value pair in the archive's metadata storage + :param string key: key value to associate the Metadata object with + :param Varies md: object to store. + :Example: + + >>> ta: TypeArchive + >>> ta.store_metadata("ordinals", {"9": "htons"}) + >>> ta.query_metadata("ordinals")["9"] + "htons" + """ + if not isinstance(md, metadata.Metadata): + md = metadata.Metadata(md) + if not core.BNTypeArchiveStoreMetadata(self.handle, key, md.handle): + raise RuntimeError("BNTypeArchiveStoreMetadata") + + def remove_metadata(self, key: str) -> None: + """ + Delete a given metadata entry in the archive + :param string key: key associated with metadata + :Example: + + >>> ta: TypeArchive + >>> ta.store_metadata("integer", 1337) + >>> ta.remove_metadata("integer") + """ + core.BNTypeArchiveRemoveMetadata(self.handle, key) + + def serialize_snapshot(self, snapshot: str) -> 'databuffer.DataBuffer': + """ + Turn a given snapshot into a data stream + :param snapshot: Snapshot id + :return: Buffer containing serialized snapshot data + """ + result = core.BNTypeArchiveSerializeSnapshot(self.handle, snapshot) + if not result: + raise RuntimeError("BNTypeArchiveDeserializeSnapshot") + return databuffer.DataBuffer(handle=result) + + def deserialize_snapshot(self, data: 'databuffer.DataBufferInputType') -> str: + """ + Take a serialized snapshot data stream and create a new snapshot from it + :param data: Snapshot data + :return: String of created snapshot id + """ + buffer = databuffer.DataBuffer(data) + result = core.BNTypeArchiveDeserializeSnapshot(self.handle, buffer.handle) + if not result: + raise RuntimeError("BNTypeArchiveDeserializeSnapshot") + return result + + def register_notification(self, notify: 'TypeArchiveNotification') -> None: + """ + Register a notification listener + :param notify: Object to receive notifications + """ + cb = TypeArchiveNotificationCallbacks(self, notify) + cb._register() + self._notifications[notify] = cb + + def unregister_notification(self, notify: 'TypeArchiveNotification') -> None: + """ + Unregister a notification listener + :param notify: Object to no longer receive notifications + """ + if notify in self._notifications: + self._notifications[notify]._unregister() + del self._notifications[notify] + + +class TypeArchiveNotification: + def __init__(self): + pass + + def view_attached(self, archive: 'TypeArchive', view: 'binaryview.BinaryView') -> None: + """ + Called when a new view attaches to the type archive. + :param archive: Source Type archive + :param view: View attaching the archive + """ + pass + + def view_detached(self, archive: 'TypeArchive', view: 'binaryview.BinaryView') -> None: + """ + Called when a view that has previously attached the archive detaches it + :param archive: Source Type archive + :param view: View detaching the archive + """ + pass + + def type_added(self, archive: 'TypeArchive', id: str, definition: 'ty_.Type') -> None: + """ + Called when a type is added to the archive + :param archive: Source Type archive + :param id: Id of type added + :param definition: Definition of type + """ + pass + + def type_updated(self, archive: 'TypeArchive', id: str, old_definition: 'ty_.Type', new_definition: 'ty_.Type') -> None: + """ + Called when a type in the archive is updated to a new definition + :param archive: Source Type archive + :param id: Id of type + :param old_definition: Previous definition + :param new_definition: Current definition + """ + pass + + def type_renamed(self, archive: 'TypeArchive', id: str, old_name: 'ty_.QualifiedName', new_name: 'ty_.QualifiedName') -> None: + """ + Called when a type in the archive is renamed + :param archive: Source Type archive + :param id: Type id + :param old_name: Previous name + :param new_name: Current name + """ + pass + + def type_deleted(self, archive: 'TypeArchive', id: str, definition: 'ty_.Type') -> None: + """ + Called when a type in the archive is deleted from the archive + :param archive: Source Type archive + :param id: Id of type deleted + :param definition: Definition of type deleted + """ + pass + + +class TypeArchiveNotificationCallbacks: + def __init__(self, archive: 'TypeArchive', notify: 'TypeArchiveNotification'): + self._archive = archive + self._notify = notify + self._cb = core.BNTypeArchiveNotification() + self._cb.context = 0 + self._cb.typeAdded = self._cb.typeAdded.__class__(self._type_added) + self._cb.typeUpdated = self._cb.typeUpdated.__class__(self._type_updated) + self._cb.typeRenamed = self._cb.typeRenamed.__class__(self._type_renamed) + self._cb.typeDeleted = self._cb.typeDeleted.__class__(self._type_deleted) + + def _register(self) -> None: + core.BNRegisterTypeArchiveNotification(self._archive.handle, self._cb) + + def _unregister(self) -> None: + core.BNUnregisterTypeArchiveNotification(self._archive.handle, self._cb) + + def _view_attached(self, ctxt, archive: ctypes.POINTER(core.BNTypeArchive), view: ctypes.POINTER(core.BNBinaryView)) -> None: + try: + self._notify.view_attached(self._archive, binaryview.BinaryView(handle=core.BNNewViewReference(view))) + except: + log.log_error(traceback.format_exc()) + + def _view_detached(self, ctxt, archive: ctypes.POINTER(core.BNTypeArchive), view: ctypes.POINTER(core.BNBinaryView)) -> None: + try: + self._notify.view_detached(self._archive, binaryview.BinaryView(handle=core.BNNewViewReference(view))) + except: + log.log_error(traceback.format_exc()) + + def _type_added(self, ctxt, archive: ctypes.POINTER(core.BNTypeArchive), id: ctypes.c_char_p, definition: ctypes.POINTER(core.BNType)) -> None: + try: + self._notify.type_added(self._archive, core.pyNativeStr(id), ty_.Type.create(handle=core.BNNewTypeReference(definition))) + except: + log.log_error(traceback.format_exc()) + + def _type_updated(self, ctxt, archive: ctypes.POINTER(core.BNTypeArchive), id: ctypes.c_char_p, old_definition: ctypes.POINTER(core.BNType), new_definition: ctypes.POINTER(core.BNType)) -> None: + try: + self._notify.type_updated(self._archive, core.pyNativeStr(id), ty_.Type.create(handle=core.BNNewTypeReference(old_definition)), ty_.Type.create(handle=core.BNNewTypeReference(new_definition))) + except: + log.log_error(traceback.format_exc()) + + def _type_renamed(self, ctxt, archive: ctypes.POINTER(core.BNTypeArchive), id: ctypes.c_char_p, old_name: ctypes.POINTER(core.BNQualifiedName), new_name: ctypes.POINTER(core.BNQualifiedName)) -> None: + try: + self._notify.type_renamed(self._archive, core.pyNativeStr(id), ty_.QualifiedName._from_core_struct(old_name.contents), ty_.QualifiedName._from_core_struct(new_name.contents)) + except: + log.log_error(traceback.format_exc()) + + def _type_deleted(self, ctxt, archive: ctypes.POINTER(core.BNTypeArchive), id: ctypes.c_char_p, definition: ctypes.POINTER(core.BNType)) -> None: + try: + self._notify.type_deleted(self._archive, core.pyNativeStr(id), ty_.Type.create(handle=core.BNNewTypeReference(definition))) + except: + log.log_error(traceback.format_exc()) + + @property + def archive(self) -> 'TypeArchive': + return self._archive + + @property + def notify(self) -> 'TypeArchiveNotification': + return self._notify
\ No newline at end of file diff --git a/typearchive.cpp b/typearchive.cpp new file mode 100644 index 00000000..29dd0d6a --- /dev/null +++ b/typearchive.cpp @@ -0,0 +1,635 @@ +// Copyright (c) 2015-2023 Vector 35 Inc +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "binaryninjaapi.h" + +using namespace BinaryNinja; + + +void TypeArchiveNotification::OnTypeAddedCallback(void* ctx, BNTypeArchive* archive, const char* id, BNType* definition) +{ + TypeArchiveNotification* notify = reinterpret_cast<TypeArchiveNotification*>(ctx); + Ref<TypeArchive> cppArchive = new TypeArchive(BNNewTypeArchiveReference(archive)); + Ref<Type> cppDefinition = new Type(BNNewTypeReference(definition)); + notify->OnTypeAdded(cppArchive, id, cppDefinition); +} + + +void TypeArchiveNotification::OnTypeUpdatedCallback(void* ctx, BNTypeArchive* archive, const char* id, BNType* oldDefinition, BNType* newDefinition) +{ + TypeArchiveNotification* notify = reinterpret_cast<TypeArchiveNotification*>(ctx); + Ref<TypeArchive> cppArchive = new TypeArchive(BNNewTypeArchiveReference(archive)); + Ref<Type> cppOldDefinition = new Type(BNNewTypeReference(oldDefinition)); + Ref<Type> cppNewDefinition = new Type(BNNewTypeReference(newDefinition)); + notify->OnTypeUpdated(cppArchive, id, cppOldDefinition, cppNewDefinition); +} + + +void TypeArchiveNotification::OnTypeRenamedCallback(void* ctx, BNTypeArchive* archive, const char* id, const BNQualifiedName* oldName, const BNQualifiedName* newName) +{ + TypeArchiveNotification* notify = reinterpret_cast<TypeArchiveNotification*>(ctx); + Ref<TypeArchive> cppArchive = new TypeArchive(BNNewTypeArchiveReference(archive)); + QualifiedName appOldName = QualifiedName::FromAPIObject(oldName); + QualifiedName appNewName = QualifiedName::FromAPIObject(newName); + notify->OnTypeRenamed(cppArchive, id, oldName, newName); +} + + +void TypeArchiveNotification::OnTypeDeletedCallback(void* ctx, BNTypeArchive* archive, const char* id, BNType* definition) +{ + TypeArchiveNotification* notify = reinterpret_cast<TypeArchiveNotification*>(ctx); + Ref<TypeArchive> cppArchive = new TypeArchive(BNNewTypeArchiveReference(archive)); + Ref<Type> cppDefinition = new Type(BNNewTypeReference(definition)); + notify->OnTypeDeleted(cppArchive, id, cppDefinition); +} + + +TypeArchiveNotification::TypeArchiveNotification() +{ + m_callbacks.context = this; + m_callbacks.typeAdded = OnTypeAddedCallback; + m_callbacks.typeUpdated = OnTypeUpdatedCallback; + m_callbacks.typeRenamed = OnTypeRenamedCallback; + m_callbacks.typeDeleted = OnTypeDeletedCallback; +} + + +TypeArchive::TypeArchive(BNTypeArchive* archive) +{ + m_object = archive; +} + + +Ref<TypeArchive> TypeArchive::Open(const std::string& path) +{ + BNTypeArchive* handle = BNOpenTypeArchive(path.c_str()); + if (!handle) + return nullptr; + return new TypeArchive(handle); +} + + +Ref<TypeArchive> TypeArchive::Create(const std::string& path, Ref<Platform> platform) +{ + BNTypeArchive* handle = BNCreateTypeArchive(path.c_str(), platform->GetObject()); + if (!handle) + return nullptr; + return new TypeArchive(handle); +} + + +Ref<TypeArchive> TypeArchive::CreateWithId(const std::string& path, Ref<Platform> platform, const std::string& id) +{ + BNTypeArchive* handle = BNCreateTypeArchiveWithId(path.c_str(), platform->GetObject(), id.c_str()); + if (!handle) + return nullptr; + return new TypeArchive(handle); +} + + +Ref<TypeArchive> TypeArchive::LookupById(const std::string& id) +{ + BNTypeArchive* handle = BNLookupTypeArchiveById(id.c_str()); + if (!handle) + return nullptr; + return new TypeArchive(handle); +} + + +void TypeArchive::Close(Ref<TypeArchive> archive) +{ + BNCloseTypeArchive(archive->GetObject()); +} + + +bool TypeArchive::IsTypeArchive(const std::string& path) +{ + return BNIsTypeArchive(path.c_str()); +} + + +std::string TypeArchive::GetId() const +{ + char* str = BNGetTypeArchiveId(m_object); + std::string result(str); + BNFreeString(str); + return result; +} + + +std::string TypeArchive::GetPath() const +{ + char* str = BNGetTypeArchivePath(m_object); + std::string result(str); + BNFreeString(str); + return result; +} + + +Ref<Platform> TypeArchive::GetPlatform() const +{ + BNPlatform* platform = BNGetTypeArchivePlatform(m_object); + if (!platform) + return nullptr; + return new Platform(platform); +} + + +std::string TypeArchive::GetCurrentSnapshotId() const +{ + char* str = BNGetTypeArchiveCurrentSnapshotId(m_object); + if (!str) + throw ExceptionWithStackTrace("BNGetTypeArchiveCurrentSnapshotId"); + std::string result(str); + BNFreeString(str); + return result; +} + + +void TypeArchive::SetCurrentSnapshot(const std::string& id) +{ + BNSetTypeArchiveCurrentSnapshot(m_object, id.c_str()); +} + + +std::vector<std::string> TypeArchive::GetAllSnapshotIds() const +{ + size_t count = 0; + char** ids = BNGetTypeArchiveAllSnapshotIds(m_object, &count); + if (!ids) + throw ExceptionWithStackTrace("BNGetTypeArchiveAllSnapshotIds"); + + std::vector<std::string> result; + for (size_t i = 0; i < count; i ++) + { + result.push_back(ids[i]); + } + + BNFreeStringList(ids, count); + return result; +} + + +std::vector<std::string> TypeArchive::GetSnapshotParentIds(const std::string& id) const +{ + size_t count = 0; + char** ids = BNGetTypeArchiveSnapshotParentIds(m_object, id.c_str(), &count); + if (!ids) + throw ExceptionWithStackTrace("BNGetTypeArchiveSnapshotParentIds"); + + std::vector<std::string> result; + for (size_t i = 0; i < count; i ++) + { + result.push_back(ids[i]); + } + + BNFreeStringList(ids, count); + return result; +} + + +std::vector<std::string> TypeArchive::GetSnapshotChildIds(const std::string& id) const +{ + size_t count = 0; + char** ids = BNGetTypeArchiveSnapshotChildIds(m_object, id.c_str(), &count); + if (!ids) + throw ExceptionWithStackTrace("BNGetTypeArchiveSnapshotChildIds"); + + std::vector<std::string> result; + for (size_t i = 0; i < count; i ++) + { + result.push_back(ids[i]); + } + + BNFreeStringList(ids, count); + return result; +} + + +TypeContainer TypeArchive::GetTypeContainer() const +{ + return TypeContainer(BNGetTypeArchiveTypeContainer(m_object)); +} + + +bool TypeArchive::AddTypes(const std::vector<QualifiedNameAndType>& types) +{ + std::vector<BNQualifiedNameAndType> apiTypes; + for (auto& type : types) + { + BNQualifiedNameAndType qnat; + qnat.name = type.name.GetAPIObject(); + qnat.type = type.type->GetObject(); + apiTypes.push_back(qnat); + } + bool result = BNAddTypeArchiveTypes(m_object, apiTypes.data(), apiTypes.size()); + for (auto& type: apiTypes) + { + QualifiedName::FreeAPIObject(&type.name); + } + return result; +} + + +bool TypeArchive::RenameType(const std::string& id, const QualifiedName& newName) +{ + BNQualifiedName qname = newName.GetAPIObject(); + bool result = BNRenameTypeArchiveType(m_object, id.c_str(), &qname); + QualifiedName::FreeAPIObject(&qname); + return result; +} + + +bool TypeArchive::DeleteType(const std::string& id) +{ + return BNDeleteTypeArchiveType(m_object, id.c_str()); +} + + +Ref<Type> TypeArchive::GetTypeById(const std::string& id, std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + BNType* type = BNGetTypeArchiveTypeById(m_object, id.c_str(), snapshot.c_str()); + if (!type) + return nullptr; + return new Type(type); +} + + +Ref<Type> TypeArchive::GetTypeByName(const QualifiedName& name, std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + BNQualifiedName qname = name.GetAPIObject(); + BNType* type = BNGetTypeArchiveTypeByName(m_object, &qname, snapshot.c_str()); + QualifiedName::FreeAPIObject(&qname); + if (!type) + return nullptr; + return new Type(type); +} + + +std::string TypeArchive::GetTypeId(const QualifiedName& name, std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + BNQualifiedName qname = name.GetAPIObject(); + char* id = BNGetTypeArchiveTypeId(m_object, &qname, snapshot.c_str()); + QualifiedName::FreeAPIObject(&qname); + if (!id) + return ""; + std::string result = id; + BNFreeString(id); + return result; + +} + + +QualifiedName TypeArchive::GetTypeName(const std::string& id, std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + BNQualifiedName qname = BNGetTypeArchiveTypeName(m_object, id.c_str(), snapshot.c_str()); + QualifiedName result = QualifiedName::FromAPIObject(&qname); + BNFreeQualifiedName(&qname); + return result; +} + + +std::unordered_map<std::string, QualifiedNameAndType> TypeArchive::GetTypes(std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + size_t count = 0; + BNQualifiedNameTypeAndId* types = BNGetTypeArchiveTypes(m_object, snapshot.c_str(), &count); + if (!types) + throw ExceptionWithStackTrace("BNGetTypeArchiveTypes"); + + std::unordered_map<std::string, QualifiedNameAndType> result; + for (size_t i = 0; i < count; ++i) + { + std::string id = types[i].id; + QualifiedNameAndType qnat; + qnat.name = QualifiedName::FromAPIObject(&types[i].name); + qnat.type = new Type(BNNewTypeReference(types[i].type)); + result.emplace(id, qnat); + } + BNFreeTypeIdList(types, count); + return result; +} + + +std::vector<std::string> TypeArchive::GetTypeIds(std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + size_t count = 0; + char** ids = BNGetTypeArchiveTypeIds(m_object, snapshot.c_str(), &count); + if (!ids) + throw ExceptionWithStackTrace("BNGetTypeArchiveTypeIds"); + + std::vector<std::string> result; + for (size_t i = 0; i < count; ++i) + { + result.push_back(ids[i]); + } + BNFreeStringList(ids, count); + return result; +} + + +std::vector<QualifiedName> TypeArchive::GetTypeNames(std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + size_t count = 0; + BNQualifiedName* names = BNGetTypeArchiveTypeNames(m_object, snapshot.c_str(), &count); + if (!names) + throw ExceptionWithStackTrace("BNGetTypeArchiveTypeNames"); + + std::vector<QualifiedName> result; + for (size_t i = 0; i < count; ++i) + { + result.push_back(QualifiedName::FromAPIObject(&names[i])); + } + BNFreeTypeNameList(names, count); + return result; +} + + +std::unordered_map<std::string, QualifiedName> TypeArchive::GetTypeNamesAndIds(std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + BNQualifiedName* names = nullptr; + char** ids = nullptr; + size_t count = 0; + if (!BNGetTypeArchiveTypeNamesAndIds(m_object, snapshot.c_str(), &names, &ids, &count)) + throw ExceptionWithStackTrace("BNGetTypeArchiveTypeNamesAndIds"); + + std::unordered_map<std::string, QualifiedName> result; + for (size_t i = 0; i < count; ++i) + { + result.emplace(ids[i], QualifiedName::FromAPIObject(&names[i])); + } + BNFreeTypeNameList(names, count); + BNFreeStringList(ids, count); + return result; +} + + +std::unordered_set<std::string> TypeArchive::GetOutgoingDirectTypeReferences(const std::string& id, std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + size_t count = 0; + char** ids = BNGetTypeArchiveOutgoingDirectTypeReferences(m_object, id.c_str(), snapshot.c_str(), &count); + if (!ids) + throw ExceptionWithStackTrace("BNGetTypeArchiveOutgoingDirectTypeReferences"); + + std::unordered_set<std::string> result; + for (size_t i = 0; i < count; ++i) + { + result.insert(ids[i]); + } + BNFreeStringList(ids, count); + return result; +} + + +std::unordered_set<std::string> TypeArchive::GetOutgoingRecursiveTypeReferences(const std::string& id, std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + size_t count = 0; + char** ids = BNGetTypeArchiveOutgoingRecursiveTypeReferences(m_object, id.c_str(), snapshot.c_str(), &count); + if (!ids) + throw ExceptionWithStackTrace("BNGetTypeArchiveOutgoingRecursiveTypeReferences"); + + std::unordered_set<std::string> result; + for (size_t i = 0; i < count; ++i) + { + result.insert(ids[i]); + } + BNFreeStringList(ids, count); + return result; +} + + +std::unordered_set<std::string> TypeArchive::GetIncomingDirectTypeReferences(const std::string& id, std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + size_t count = 0; + char** ids = BNGetTypeArchiveIncomingDirectTypeReferences(m_object, id.c_str(), snapshot.c_str(), &count); + if (!ids) + throw ExceptionWithStackTrace("BNGetTypeArchiveIncomingDirectTypeReferences"); + + std::unordered_set<std::string> result; + for (size_t i = 0; i < count; ++i) + { + result.insert(ids[i]); + } + BNFreeStringList(ids, count); + return result; +} + + +std::unordered_set<std::string> TypeArchive::GetIncomingRecursiveTypeReferences(const std::string& id, std::string snapshot) const +{ + if (snapshot.empty()) + snapshot = GetCurrentSnapshotId(); + size_t count = 0; + char** ids = BNGetTypeArchiveIncomingRecursiveTypeReferences(m_object, id.c_str(), snapshot.c_str(), &count); + if (!ids) + throw ExceptionWithStackTrace("BNGetTypeArchiveIncomingRecursiveTypeReferences"); + + std::unordered_set<std::string> result; + for (size_t i = 0; i < count; ++i) + { + result.insert(ids[i]); + } + BNFreeStringList(ids, count); + return result; +} + + +struct SnapshotContext +{ + std::function<void(const std::string& id)> func; +}; + + +bool SnapshotContextCallback(void* context, const char* id) +{ + SnapshotContext* ctxt = (SnapshotContext*)context; + try + { + ctxt->func(id); + return true; + } + catch (...) + { + return false; + } +} + + +std::string TypeArchive::NewSnapshotTransaction(std::function<void(const std::string& id)> func, const std::vector<std::string>& parents) +{ + SnapshotContext ctxt; + ctxt.func = func; + + std::vector<const char*> apiParents; + for (const auto& parent: parents) + { + apiParents.push_back(parent.c_str()); + } + + char* snapshotId = BNTypeArchiveNewSnapshotTransaction(m_object, SnapshotContextCallback, (void*)&ctxt, apiParents.data(), apiParents.size()); + if (!snapshotId) + { + throw ExceptionWithStackTrace("BNTypeArchiveNewSnapshotTransaction"); + } + std::string result = snapshotId; + BNFreeString(snapshotId); + return result; +} + + +void TypeArchive::RegisterNotification(TypeArchiveNotification* notification) +{ + BNRegisterTypeArchiveNotification(m_object, notification->GetCallbacks()); +} + + +void TypeArchive::UnregisterNotification(TypeArchiveNotification* notification) +{ + BNUnregisterTypeArchiveNotification(m_object, notification->GetCallbacks()); +} + + +void TypeArchive::StoreMetadata(const std::string& key, Ref<Metadata> value) +{ + if (!BNTypeArchiveStoreMetadata(m_object, key.c_str(), value->GetObject())) + throw ExceptionWithStackTrace("BNTypeArchiveStoreMetadata"); +} + + +Ref<Metadata> TypeArchive::QueryMetadata(const std::string& key) const +{ + BNMetadata* metadata = BNTypeArchiveQueryMetadata(m_object, key.c_str()); + if (!metadata) + return nullptr; + return new Metadata(metadata); +} + + +void TypeArchive::RemoveMetadata(const std::string& key) +{ + if (!BNTypeArchiveRemoveMetadata(m_object, key.c_str())) + throw ExceptionWithStackTrace("BNTypeArchiveRemoveMetadata"); +} + + +DataBuffer TypeArchive::SerializeSnapshot(const std::string& snapshot) const +{ + BNDataBuffer* buffer = BNTypeArchiveSerializeSnapshot(m_object, snapshot.c_str()); + if (!buffer) + throw ExceptionWithStackTrace("BNTypeArchiveSerializeSnapshot"); + return DataBuffer(buffer); +} + + +std::string TypeArchive::DeserializeSnapshot(const DataBuffer& data) +{ + char* id = BNTypeArchiveDeserializeSnapshot(m_object, data.GetBufferObject()); + if (!id) + throw ExceptionWithStackTrace("BNTypeArchiveDeserializeSnapshot"); + std::string result = id; + BNFreeString(id); + return result; +} + + +std::optional<std::string> TypeArchive::MergeSnapshots( + const std::string& baseSnapshot, + const std::string& firstSnapshot, + const std::string& secondSnapshot, + const std::unordered_map<std::string, std::string>& mergeConflictsIn, + std::unordered_set<std::string>& mergeConflictsOut, + std::function<bool(size_t, size_t)> progress +) +{ + std::vector<const char*> mergeConflictKeysIn; + std::vector<const char*> mergeConflictValuesIn; + for (auto& [key, value]: mergeConflictsIn) + { + mergeConflictKeysIn.push_back(key.data()); + mergeConflictValuesIn.push_back(value.data()); + } + + char** mergeConflictsOutApi; + size_t mergeConflictCountOut; + + char* result; + + ProgressContext progressContext; + progressContext.callback = progress; + + if (!BNTypeArchiveMergeSnapshots( + m_object, + baseSnapshot.c_str(), + firstSnapshot.c_str(), + secondSnapshot.c_str(), + mergeConflictKeysIn.data(), + mergeConflictValuesIn.data(), + mergeConflictsIn.size(), + &mergeConflictsOutApi, + &mergeConflictCountOut, + &result, + ProgressCallback, + &progressContext + )) + { + throw ExceptionWithStackTrace("BNTypeArchiveMergeSnapshots"); + } + + mergeConflictsOut.clear(); + for (size_t i = 0; i < mergeConflictCountOut; i ++) + { + mergeConflictsOut.insert(mergeConflictsOutApi[i]); + } + BNFreeStringList(mergeConflictsOutApi, mergeConflictCountOut); + + std::optional<std::string> resultCpp; + if (result) + { + resultCpp = result; + BNFreeString(result); + } + else + { + resultCpp = std::nullopt; + } + + return resultCpp; +}
\ No newline at end of file diff --git a/typecontainer.cpp b/typecontainer.cpp index ba411b83..bd31a134 100644 --- a/typecontainer.cpp +++ b/typecontainer.cpp @@ -50,6 +50,13 @@ TypeContainer::TypeContainer(Ref<TypeLibrary> library) } +TypeContainer::TypeContainer(Ref<TypeArchive> archive) +{ + auto container = archive->GetTypeContainer(); + m_object = BNDuplicateTypeContainer(container.m_object); +} + + TypeContainer::TypeContainer(Ref<Platform> platform) { auto container = platform->GetTypeContainer(); @@ -322,6 +329,44 @@ std::optional<std::unordered_map<std::string, QualifiedName>> TypeContainer::Get } +bool TypeContainer::ParseTypeString( + const std::string& source, + BinaryNinja::QualifiedNameAndType& result, + std::vector<TypeParserError>& errors +) +{ + BNQualifiedNameAndType apiResult; + BNTypeParserError* apiErrors; + size_t errorCount; + + auto success = BNTypeContainerParseTypeString(m_object, source.c_str(), &apiResult, + &apiErrors, &errorCount); + + for (size_t j = 0; j < errorCount; j ++) + { + TypeParserError error; + error.severity = apiErrors[j].severity, + error.message = apiErrors[j].message, + error.fileName = apiErrors[j].fileName, + error.line = apiErrors[j].line, + error.column = apiErrors[j].column, + errors.push_back(error); + } + BNFreeTypeParserErrors(apiErrors, errorCount); + + if (!success) + { + return false; + } + + result.type = new Type(BNNewTypeReference(apiResult.type)); + result.name = QualifiedName::FromAPIObject(&apiResult.name); + + BNFreeQualifiedNameAndType(&apiResult); + return true; +} + + bool TypeContainer::ParseTypesFromSource( const std::string& text, const std::string& fileName, diff --git a/ui/commands.h b/ui/commands.h index 62016bdb..441ca0ed 100644 --- a/ui/commands.h +++ b/ui/commands.h @@ -21,7 +21,7 @@ bool BINARYNINJAUIAPI inputNameForType( QWidget* parent, std::string& name, const QString& title = "Set Name", const QString& msg = "Enter name:"); bool BINARYNINJAUIAPI InferArraySize(TypeRef& type, size_t selectionSize); -bool BINARYNINJAUIAPI askForNewType(QWidget* parent, BinaryViewRef data, FunctionRef func, const std::string& title, +bool BINARYNINJAUIAPI askForNewType(QWidget* parent, std::optional<BinaryNinja::TypeContainer> container, const std::string& title, bool allowZeroSize, TypeRef& type, BinaryNinja::QualifiedName& name); bool BINARYNINJAUIAPI inputNewType(QWidget* parent, BinaryViewRef data, FunctionRef currentFunction, uint64_t currentAddr, size_t selectionSize, HighlightTokenState& highlight); @@ -51,7 +51,7 @@ uint64_t BINARYNINJAUIAPI getInnerMostStructureOffset( BinaryViewRef data, StructureRef structure, const std::vector<std::string>& nameList, size_t nameIndex); // Auto generate a structure name -std::string BINARYNINJAUIAPI createStructureName(BinaryViewRef data); +std::string BINARYNINJAUIAPI createStructureName(BinaryNinja::TypeContainer types); std::optional<BinaryNinja::Variable> BINARYNINJAUIAPI getSplitVariableForAssignment( FunctionRef func, BNFunctionGraphType ilType, uint64_t location, const BinaryNinja::Variable& var); diff --git a/ui/createstructdialog.h b/ui/createstructdialog.h index 149a0583..0e331a2a 100644 --- a/ui/createstructdialog.h +++ b/ui/createstructdialog.h @@ -28,13 +28,13 @@ class BINARYNINJAUIAPI GetStructuresListThread : public QThread std::function<void()> m_completeFunc; std::mutex m_mutex; bool m_done; - BinaryViewRef m_view; + BinaryNinja::TypeContainer m_container; protected: virtual void run() override; public: - GetStructuresListThread(BinaryViewRef view, const std::function<void()>& completeFunc); + GetStructuresListThread(BinaryNinja::TypeContainer container, const std::function<void()>& completeFunc); void cancel(); const QStringList& getTypes() const { return m_allTypes; } @@ -79,7 +79,7 @@ class BINARYNINJAUIAPI CreateStructDialog : public QDialog QCheckBox* m_propagateDataVarRefs; QCheckBox* m_pointer; - BinaryViewRef m_view; + std::optional<BinaryNinja::TypeContainer> m_typeContainer; BinaryNinja::QualifiedName m_resultName; uint64_t m_resultSize; bool m_resultDataVarRefs; @@ -95,7 +95,7 @@ class BINARYNINJAUIAPI CreateStructDialog : public QDialog virtual void customEvent(QEvent* event) override; public: - CreateStructDialog(QWidget* parent, BinaryViewRef view, const std::string& name, bool askForPointer = false, + CreateStructDialog(QWidget* parent, std::optional<BinaryNinja::TypeContainer> typeContainer, const std::string& name, bool askForPointer = false, bool defaultToPointer = false); ~CreateStructDialog(); diff --git a/ui/typebrowser.h b/ui/typebrowser.h index 006568fa..36150597 100644 --- a/ui/typebrowser.h +++ b/ui/typebrowser.h @@ -111,6 +111,7 @@ public: { None, TypeLibrary, + TypeArchive, DebugInfo, Platform, Other @@ -124,6 +125,7 @@ private: SourceType m_sourceType; std::optional<TypeLibraryRef> m_sourceLibrary; + std::optional<TypeArchiveRef> m_sourceArchive; std::optional<std::string> m_sourceDebugInfoParser; std::optional<PlatformRef> m_sourcePlatform; std::optional<std::string> m_sourceOtherName; @@ -179,7 +181,7 @@ protected: //----------------------------------------------------------------------------- -class BINARYNINJAUIAPI TypeBrowserModel : public QAbstractItemModel, public BinaryNinja::BinaryDataNotification +class BINARYNINJAUIAPI TypeBrowserModel : public QAbstractItemModel, public BinaryNinja::BinaryDataNotification, public BinaryNinja::TypeArchiveNotification { Q_OBJECT BinaryViewRef m_data; @@ -197,12 +199,15 @@ class BINARYNINJAUIAPI TypeBrowserModel : public QAbstractItemModel, public Bina std::map<std::string, BinaryNinja::TypeContainer> m_containers; std::map<std::string, BinaryViewRef> m_containerViews; + std::map<std::string, TypeArchiveRef> m_containerArchives; + std::map<std::string, std::string> m_containerArchiveIds; std::map<std::string, TypeLibraryRef> m_containerLibraries; std::map<std::string, DebugInfoRef> m_containerDebugInfos; std::map<std::string, PlatformRef> m_containerPlatforms; - void updateContainerList(); + void addContainer(BinaryNinja::TypeContainer cont); void callUpdateCallbacks(); + void commitUpdate(TypeBrowserTreeNode::UpdateData& update); void commitUpdates(std::vector<TypeBrowserTreeNode::UpdateData>& updates); public: @@ -217,10 +222,24 @@ public: std::optional<std::reference_wrapper<BinaryNinja::TypeContainer>> containerForContainerId(const std::string& id); std::optional<std::reference_wrapper<const BinaryNinja::TypeContainer>> containerForContainerId(const std::string& id) const; std::optional<BinaryViewRef> viewForContainerId(const std::string& id) const; + std::optional<TypeArchiveRef> archiveForContainerId(const std::string& id) const; + std::optional<std::string> archiveIdForContainerId(const std::string& id) const; std::optional<TypeLibraryRef> libraryForContainerId(const std::string& id) const; std::optional<DebugInfoRef> debugInfoForContainerId(const std::string& id) const; std::optional<PlatformRef> platformForContainerId(const std::string& id) const; + void addAllContainersForView(BinaryViewRef view); + + void addContainerForView(BinaryViewRef view); + void addUserContainerForView(BinaryViewRef view); + void addAutoContainerForView(BinaryViewRef view); + void addContainerForArchive(TypeArchiveRef archive); + void addContainerForArchiveId(const std::string& archiveId, const std::string& path); + void addContainerForLibrary(TypeLibraryRef library); + void addContainerForDebugInfo(DebugInfoRef debugInfo, const std::string& parser); + void addContainerForPlatform(PlatformRef platform); + void clearContainers(); + void updateFonts(); void runAfterUpdate(std::function<void()> callback); @@ -244,6 +263,16 @@ public: void OnTypeReferenceChanged(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override; void OnTypeFieldReferenceChanged(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, uint64_t offset) override; + void OnTypeAdded(TypeArchiveRef archive, const std::string& id, TypeRef definition) override; + void OnTypeUpdated(TypeArchiveRef archive, const std::string& id, TypeRef oldDefinition, TypeRef newDefinition) override; + void OnTypeRenamed(TypeArchiveRef archive, const std::string& id, const BinaryNinja::QualifiedName& oldName, const BinaryNinja::QualifiedName& newName) override; + void OnTypeDeleted(TypeArchiveRef archive, const std::string& id, TypeRef definition) override; + + void OnTypeArchiveAttached(BinaryNinja::BinaryView* data, const std::string& id, const std::string& path) override; + void OnTypeArchiveDetached(BinaryNinja::BinaryView* data, const std::string& id, const std::string& path) override; + void OnTypeArchiveConnected(BinaryNinja::BinaryView* data, BinaryNinja::TypeArchive* archive) override; + void OnTypeArchiveDisconnected(BinaryNinja::BinaryView* data, BinaryNinja::TypeArchive* archive) override; + Q_SIGNALS: void updatesAboutToHappen(); void updateComplete(); @@ -290,6 +319,7 @@ class BINARYNINJAUIAPI TypeBrowserItemDelegate : public QItemDelegate void initFont(); public: TypeBrowserItemDelegate(class TypeBrowserView* view); + int lineHeight() const; void updateFonts(); virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; @@ -340,6 +370,8 @@ class BINARYNINJAUIAPI TypeBrowserView : public QFrame, public View, public Filt TypeEditor* m_typeEditor; QTextEdit* m_debugText; + void updateInTransaction(std::function<bool()> transaction); + public: TypeBrowserView(BinaryViewRef data, TypeBrowserContainer* container); @@ -401,17 +433,53 @@ public: // makeSureItHasPlatform: if the type container is a BV with no platform (raw), ask for one and return nullopt if rejected // preferView: if the type container is a BV and the user/auto-only container, switch to the whole-view container for that BV instead std::optional<BinaryNinja::TypeContainer> selectedTypeContainer(bool makeSureItHasPlatform = true, bool preferView = false) const; + // Same as above, but if it returns nullopt, try again with m_data + std::optional<BinaryNinja::TypeContainer> selectedTypeContainerOrMData(bool makeSureItHasPlatform = true, bool preferView = false) const; + + // TA selected or TA relevant to selected types, only if JUST ta stuff is selected and only 1 TA + std::optional<TypeArchiveRef> selectedTA() const; + // Id of TA selected or TA relevant to selected types, only if JUST ta stuff is selected and only 1 TA + std::optional<std::string> selectedTAId() const; + // TAs selected or TAs relevant to selected types, only if JUST ta stuff is selected + std::optional<std::unordered_set<TypeArchiveRef>> selectedTAs() const; + // Ids of TAs selected or TAs relevant to selected types, only if JUST ta stuff is selected + std::optional<std::unordered_set<std::string>> selectedTAIds() const; + // If selectedTAs exist, map of ta ids to ids of selected types from that ta + std::optional<std::unordered_map<std::string, std::unordered_set<std::string>>> selectedTATypeIds() const; + // All type archives that are attached and connected + std::vector<TypeArchiveRef> connectedTAs(BinaryViewRef view) const; // Names -> Ids, if any don't exist then nullopt static std::optional<std::unordered_set<std::string>> typeIdsFromNames(BinaryViewRef view, const std::unordered_set<BinaryNinja::QualifiedName>& names); + // Ids -> Option<TypeArchive> + static std::unordered_map<std::optional<TypeArchiveRef>, std::unordered_set<std::string>> associatedTypeArchivesForTypeIds(BinaryViewRef view, const std::unordered_set<std::string>& typeIds); - std::optional<std::reference_wrapper<BinaryNinja::TypeContainer>> containerForId(const std::string& containerId, bool makeSureItHasPlatform = false, bool preferView = false); + std::optional<BinaryNinja::TypeContainer> containerForId(const std::string& containerId, bool makeSureItHasPlatform = false, bool preferView = false) const; // Menu actions static void registerActions(); void bindActions(); void showContextMenu(); + bool canConnectTypeArchive(); + void connectTypeArchive(); + + bool canCreateTypeArchive(); + void createTypeArchive(); + bool canAttachTypeArchive(); + void attachTypeArchive(); + bool canDetachTypeArchive(); + void detachTypeArchive(); + + bool canSyncSelectedTypes(); + void syncSelectedTypes(); + bool canPushSelectedTypes(); + void pushSelectedTypes(); + bool canPullSelectedTypes(); + void pullSelectedTypes(); + bool canDisassociateSelectedTypes(); + void disassociateSelectedTypes(); + bool canCreateNewTypes(); void createNewTypes(); bool canCreateNewStructure(); @@ -435,6 +503,8 @@ public: void expandAll(); bool canCollapseAll(); void collapseAll(); + bool canSwitchLayout(); + void switchLayout(); Q_SIGNALS: void typeNameNavigated(const std::string& typeName, bool newSelection); diff --git a/ui/typedialog.h b/ui/typedialog.h index 1825fdb5..f2173c93 100644 --- a/ui/typedialog.h +++ b/ui/typedialog.h @@ -36,13 +36,13 @@ class BINARYNINJAUIAPI GetTypesListThread : public QThread std::function<void()> m_completeFunc; std::mutex m_mutex; bool m_done; - BinaryViewRef m_view; + BinaryNinja::TypeContainer m_typeContainer; protected: virtual void run() override; public: - GetTypesListThread(BinaryViewRef view, const std::function<void()>& completeFunc); + GetTypesListThread(BinaryNinja::TypeContainer typeContainer, const std::function<void()>& completeFunc); void cancel(); const QStringList& getTypes() const { return m_allTypes; } @@ -58,7 +58,7 @@ class ParseTypeThread : public QThread { Q_OBJECT - BinaryViewRef m_view; + std::optional<BinaryNinja::TypeContainer> m_typeContainer; std::string m_text; void run() override; @@ -67,7 +67,7 @@ class ParseTypeThread : public QThread void parsingComplete(bool valid, BinaryNinja::QualifiedNameAndType type, QString error); public: - ParseTypeThread(BinaryViewRef view, QString text); + ParseTypeThread(std::optional<BinaryNinja::TypeContainer> typeContainer, QString text); void cancel(); }; #endif @@ -84,7 +84,7 @@ class BINARYNINJAUIAPI TypeDialog : public QDialog QStringListModel* m_model; QLabel* m_prompt; QString m_promptText; - BinaryViewRef m_view; + std::optional<BinaryNinja::TypeContainer> m_typeContainer; bool m_resultValid; QStringList m_historyEntries; int m_historySize; @@ -113,7 +113,7 @@ class BINARYNINJAUIAPI TypeDialog : public QDialog void updateTimerEvent(); public: - TypeDialog(QWidget* parent, BinaryViewRef view, const QString& title = "Specify Type", + TypeDialog(QWidget* parent, std::optional<BinaryNinja::TypeContainer> typeContainer, const QString& title = "Specify Type", const QString& prompt = "Enter Type Name", const QString& existing = ""); ~TypeDialog(); BinaryNinja::QualifiedNameAndType getType() const { return m_type; } diff --git a/ui/uitypes.h b/ui/uitypes.h index 49ef0480..d2f43868 100644 --- a/ui/uitypes.h +++ b/ui/uitypes.h @@ -107,6 +107,7 @@ typedef BinaryNinja::Ref<BinaryNinja::TagType> TagTypeRef; typedef BinaryNinja::Ref<BinaryNinja::TemporaryFile> TemporaryFileRef; typedef BinaryNinja::Ref<BinaryNinja::Transform> TransformRef; typedef BinaryNinja::Ref<BinaryNinja::Type> TypeRef; +typedef BinaryNinja::Ref<BinaryNinja::TypeArchive> TypeArchiveRef; typedef BinaryNinja::Ref<BinaryNinja::TypeLibrary> TypeLibraryRef; typedef BinaryNinja::Ref<BinaryNinja::WebsocketClient> WebsocketClientRef; typedef BinaryNinja::Ref<BinaryNinja::WebsocketProvider> WebsocketProviderRef; |
