summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.h535
-rw-r--r--binaryninjacore.h104
-rw-r--r--binaryview.cpp240
-rw-r--r--python/__init__.py1
-rw-r--r--python/binaryview.py463
-rw-r--r--python/typearchive.py758
-rw-r--r--typearchive.cpp635
-rw-r--r--typecontainer.cpp45
-rw-r--r--ui/commands.h4
-rw-r--r--ui/createstructdialog.h8
-rw-r--r--ui/typebrowser.h76
-rw-r--r--ui/typedialog.h12
-rw-r--r--ui/uitypes.h1
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;