summaryrefslogtreecommitdiff
path: root/plugins/rtti
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2024-11-22 18:16:32 -0500
committerMason Reed <mason@vector35.com>2025-03-19 21:17:34 -0400
commit9ff2b8d804a34941a6085af85b6749c20549240e (patch)
tree9b78b478476cfb66fc68828087ad9ff99cdfacb5 /plugins/rtti
parent7d66d87a4600c8954c585c3c690546fc56903d44 (diff)
Itanium RTTI scaffolding
Diffstat (limited to 'plugins/rtti')
-rw-r--r--plugins/rtti/CMakeLists.txt46
-rw-r--r--plugins/rtti/README.md86
-rw-r--r--plugins/rtti/itanium.cpp356
-rw-r--r--plugins/rtti/itanium.h136
-rw-r--r--plugins/rtti/microsoft.cpp875
-rw-r--r--plugins/rtti/microsoft.h88
-rw-r--r--plugins/rtti/plugin.cpp106
-rw-r--r--plugins/rtti/rtti.cpp112
-rw-r--r--plugins/rtti/rtti.h47
9 files changed, 1852 insertions, 0 deletions
diff --git a/plugins/rtti/CMakeLists.txt b/plugins/rtti/CMakeLists.txt
new file mode 100644
index 00000000..085f13a3
--- /dev/null
+++ b/plugins/rtti/CMakeLists.txt
@@ -0,0 +1,46 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(msvc_rtti)
+
+file(GLOB SOURCES
+ *.cpp
+ *.c
+ *.h)
+
+if(DEMO)
+ add_library(${PROJECT_NAME} STATIC ${SOURCES})
+else()
+ add_library(${PROJECT_NAME} SHARED ${SOURCES})
+endif()
+
+if(NOT BN_INTERNAL_BUILD)
+ # Out-of-tree build
+ find_path(
+ BN_API_PATH
+ NAMES binaryninjaapi.h
+ HINTS ../../.. binaryninjaapi $ENV{BN_API_PATH}
+ REQUIRED
+ )
+ add_subdirectory(${BN_API_PATH} api)
+endif()
+
+target_link_libraries(${PROJECT_NAME} binaryninjaapi)
+
+set_target_properties(${PROJECT_NAME} PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ C_STANDARD 99
+ C_STANDARD_REQUIRED ON
+ C_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON)
+
+if(BN_INTERNAL_BUILD)
+ plugin_rpath(${PROJECT_NAME})
+ set_target_properties(${PROJECT_NAME} PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+else()
+ bn_install_plugin(${PROJECT_NAME})
+endif()
diff --git a/plugins/rtti/README.md b/plugins/rtti/README.md
new file mode 100644
index 00000000..ef95222d
--- /dev/null
+++ b/plugins/rtti/README.md
@@ -0,0 +1,86 @@
+# MSVC RTTI
+
+Parses and symbolizes MSVC RTTI information in [Binary Ninja].
+
+## Example Complete Object Locator
+
+This analysis can be triggered with the `MSVC\\Find RTTI` command.
+
+```cpp
+struct _RTTICompleteObjectLocator MapTrackView::`RTTI Complete Object Locator'{for `QPaintDevice'} =
+{
+ enum signature = COL_SIG_REV1
+ uint32_t offset = 0x10
+ uint32_t cdOffset = 0x0
+ void* __based(start) pTypeDescriptor = class MapTrackView `RTTI Type Descriptor' {__dos_header + 0x2071e8}
+ struct _RTTIClassHierarchyDescriptor* __based(start) pClassHierarchyDescriptor = MapTrackView::`RTTI Class Hierarchy Descriptor' {__dos_header + 0x1c6128}
+ void* __based(start) pSelf = MapTrackView::`RTTI Complete Object Locator'{for `QPaintDevice'} {__dos_header + 0x1c61a0}
+}
+```
+
+_The above listing includes type information deduced separately through demangled names_
+
+## Example Virtual Function Table Listing
+
+This analysis can be triggered with the `MSVC\\Find VFTs` command.
+
+```cpp
+void* data_14013bfd8 = MapTrackView::`RTTI Complete Object Locator'{for `QPaintDevice'}
+struct QPaintDevice::MapTrackView::VTable MapTrackView::`vftable'{for `QPaintDevice'} =
+{
+ int64_t (* const vFunc_0)(int64_t arg1, char arg2, int512_t arg3) = sub_140053114
+ int32_t (* const vFunc_1)(QWidget* this) = Qt5Widgets:QWidget::devType(QWidget* this) const__ptr64
+ class QPaintEngine* __ptr64 (* const vFunc_2)(QWidget* this) = Qt5Widgets:QWidget::paintEngine(QWidget* this) const__ptr64
+ int32_t (* const vFunc_3)(QWidget* this, enum QPaintDevice::PaintDeviceMetric arg2) = Qt5Widgets:QWidget::metric(QWidget* this, enum QPaintDevice::PaintDeviceMetric) const__ptr64
+ void (* const vFunc_4)(QWidget* this, class QPainter* __ptr64 arg2) = Qt5Widgets:QWidget::initPainter(QWidget* this, class QPainter* __ptr64) const__ptr64
+ class QPaintDevice* __ptr64 (* const vFunc_5)(QWidget* this, class QPoint* __ptr64 arg2) = Qt5Widgets:QWidget::redirected(QWidget* this, class QPoint* __ptr64) const__ptr64
+ class QPainter* __ptr64 (* const vFunc_6)(QWidget* this) = Qt5Widgets:QWidget::sharedPainter(QWidget* this) const__ptr64
+}
+```
+
+_The above listing includes type information deduced separately through demangled names_
+
+## Exposed Metadata
+
+This plugin will store metadata on the view queryable view the `msvc` key.
+
+### Example Metadata
+
+```py
+# data = bv.query_metadata("msvc")
+data = {
+ "classes": {
+ "5368823328": {
+ "className": "Animal",
+ "vft": {
+ "address": 5368818736,
+ "functions": [{"address": 5368779647}, {"address": 5368779152}],
+ },
+ },
+ "5368823464": {
+ "className": "Flying",
+ "vft": {"address": 5368818768, "functions": [{"address": 5368778982}]},
+ },
+ "5368823600": {
+ "baseClassName": "Animal",
+ "className": "Bird",
+ "vft": {
+ "address": 5368818816,
+ "functions": [{"address": 5368779137}, {"address": 5368779272}],
+ },
+ },
+ "5368823808": {
+ "baseClassName": "Flying",
+ "className": "Bird",
+ "classOffset": 16,
+ "vft": {"address": 5368818848, "functions": [{"address": 5368778982}]},
+ },
+ "5368823856": {
+ "className": "type_info",
+ "vft": {"address": 5368818888, "functions": [{"address": 5368778927}]},
+ },
+ }
+}
+```
+
+[Binary Ninja]: https://binary.ninja \ No newline at end of file
diff --git a/plugins/rtti/itanium.cpp b/plugins/rtti/itanium.cpp
new file mode 100644
index 00000000..cd80b7f9
--- /dev/null
+++ b/plugins/rtti/itanium.cpp
@@ -0,0 +1,356 @@
+#include "itanium.h"
+
+using namespace BinaryNinja;
+using namespace BinaryNinja::RTTI;
+using namespace BinaryNinja::RTTI::Itanium;
+
+// TODO: Need to add the boiler plate stuff
+// TODO: Can we find the object offset for the vtable entry?
+// TODO: Itanium doesnt really say anything about the sizing of these fields, i assume they are all u32 for thje most part.
+
+constexpr const char *TYPE_SOURCE_ITANIUM = "rtti_itanium";
+
+TypeInfo::TypeInfo(BinaryView *view, uint64_t address)
+{
+ BinaryReader reader = BinaryReader(view);
+ reader.Seek(address);
+ base = reader.ReadPointer();
+ auto typeNameAddr = reader.ReadPointer();
+ reader.Seek(typeNameAddr);
+ type_name = reader.ReadCString(512);
+}
+
+
+SIClassTypeInfo::SIClassTypeInfo(BinaryView *view, uint64_t address) : ClassTypeInfo(view, address)
+{
+ BinaryReader reader = BinaryReader(view);
+ // TODO: Manually seeking to the offset is ugly.
+ reader.Seek(address + 0x10);
+ base_type = reader.ReadPointer();
+}
+
+
+BaseClassTypeInfo::BaseClassTypeInfo(BinaryView *view, uint64_t address)
+{
+ BinaryReader reader = BinaryReader(view);
+ reader.Seek(address);
+ base_type = reader.ReadPointer();
+ offset_flags = reader.Read32();
+ // TODO: Test this...
+ offset_flags_masks = static_cast<OffsetFlagsMasks>(reader.Read32());
+}
+
+
+VMIClassTypeInfo::VMIClassTypeInfo(BinaryView *view, uint64_t address) : ClassTypeInfo(view, address)
+{
+ BinaryReader reader = BinaryReader(view);
+ // TODO: Manually seeking to the offset is ugly.
+ reader.Seek(address + 0x10);
+ flags = reader.Read32();
+ base_count = reader.Read32();
+ base_info = {};
+ for (size_t i = 1; i < base_count; i++)
+ {
+ // TODO: Verify this is correct.
+ uint64_t currentBaseAddr = reader.GetOffset();
+ base_info.emplace_back(view, reader.GetOffset());
+ reader.Seek(currentBaseAddr + 12);
+ }
+}
+
+
+Ref<Type> TypeInfoType(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("TypeInfo"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+
+ StructureBuilder structureBuilder;
+ Ref<Type> pBaseType = Type::PointerType(arch, Type::VoidType());
+ structureBuilder.AddMember(pBaseType, "__base");
+ Ref<Type> pTypeNameType = Type::PointerType(arch, Type::IntegerType(1, true, "char"));
+ structureBuilder.AddMember(pTypeNameType, "__type_name");
+
+ Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
+ // TODO: std::type_info or __cxxabiv1::__type_info ?
+ view->DefineType(typeId, QualifiedName("std::type_info"), structureType);
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> ClassTypeInfoType(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("ClassTypeInfo"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ StructureBuilder structureBuilder;
+ BaseStructure typeInfoBase = BaseStructure(TypeInfoType(view), 0);
+ structureBuilder.SetBaseStructures({typeInfoBase});
+ // TODO: This exists because if you have no members but a base struct things get screwy.
+ structureBuilder.SetWidth(0x10);
+
+ Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
+ view->DefineType(typeId, QualifiedName("__cxxabiv1::__class_type_info"), structureType);
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+Ref<Type> SIClassTypeInfoType(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("SIClassTypeInfo"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+
+ StructureBuilder structureBuilder;
+ Ref<Type> pBaseType = Type::PointerType(arch, Type::VoidType());
+ structureBuilder.AddMemberAtOffset(pBaseType, "__base_type", 0x10);
+ BaseStructure classTypeInfoBase = BaseStructure(ClassTypeInfoType(view), 0);
+ structureBuilder.SetBaseStructures({classTypeInfoBase});
+
+ Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
+ view->DefineType(typeId, QualifiedName("__cxxabiv1::__si_class_type_info"), structureType);
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> OffsetFlagsMasksType(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("OffsetFlagsMasks"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+ Ref<Type> uintType = Type::IntegerType(4, false);
+
+ EnumerationBuilder enumerationBuilder;
+ enumerationBuilder.AddMemberWithValue("__virtual_mask", 0x1);
+ enumerationBuilder.AddMemberWithValue("__public_mask", 0x2);
+ enumerationBuilder.AddMemberWithValue("__offset_shift", 0x8);
+
+ Ref<Type> enumerationType = TypeBuilder::EnumerationType(arch, enumerationBuilder.Finalize()).Finalize();
+ view->DefineType(typeId, QualifiedName("__cxxabiv1::__offset_flags_masks"), enumerationType);
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> BaseClassTypeInfoType(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("BaseClassTypeInfo"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+ Ref<Type> uintType = Type::IntegerType(4, false);
+
+ StructureBuilder structureBuilder;
+ Ref<Type> pBaseType = Type::PointerType(arch, Type::VoidType());
+ structureBuilder.AddMember(pBaseType, "__base_type");
+ structureBuilder.AddMember(uintType, "__offset_flags");
+ structureBuilder.AddMember(OffsetFlagsMasksType(view), "__offset_flags_masks");
+
+ Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
+ view->DefineType(typeId, QualifiedName("__cxxabiv1::__base_class_type_info"), structureType);
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> VMIClassTypeInfoType(BinaryView *view, int baseCount)
+{
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+ Ref<Type> uintType = Type::IntegerType(4, false);
+
+ StructureBuilder structureBuilder;
+ structureBuilder.AddMemberAtOffset(uintType, "__flags", 0x10);
+ structureBuilder.AddMemberAtOffset(uintType, "__base_count", 0x14);
+ Ref<Type> baseInfoType = Type::ArrayType(BaseClassTypeInfoType(view), baseCount);
+ structureBuilder.AddMemberAtOffset(baseInfoType, "__base_info", 0x18);
+ BaseStructure classTypeInfoBase = BaseStructure(ClassTypeInfoType(view), 0);
+ structureBuilder.SetBaseStructures({classTypeInfoBase});
+
+ return TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
+}
+
+
+std::optional<TypeInfoVariant> ReadTypeInfoVariant(BinaryView *view, uint64_t objectAddr)
+{
+ auto typeInfo = TypeInfo(view, objectAddr);
+
+ // TODO: What if there is no symbol?
+ // If there is a symbol at objectAddr pointing to a symbol starting with "vtable for __cxxabiv1"
+ auto baseSym = view->GetSymbolByAddress(typeInfo.base);
+ if (baseSym == nullptr)
+ return std::nullopt;
+ if (baseSym->GetType() != ExternalSymbol)
+ return std::nullopt;
+ auto baseSymName = baseSym->GetShortName();
+
+ // TODO: __vmi_class_type_info seems to point to operator delete(void*)
+ // TODO: For now we just bruteforce it with the type_name check...
+
+ if (baseSymName.find("__cxxabiv1") != std::string::npos)
+ {
+ // symbol takes the form of `abi::base_name`
+ auto baseTyStartPos = baseSymName.find("::");
+ if (baseTyStartPos != std::string::npos)
+ baseSymName = baseSymName.substr(baseTyStartPos + 2);
+
+ if (baseSymName == "__class_type_info")
+ return TIVClass;
+ if (baseSymName == "__si_class_type_info")
+ return TIVSIClass;
+ if (baseSymName == "__vmi_class_type_info")
+ return TIVVMIClass;
+ }
+ else if (typeInfo.type_name.length() > 2)
+ {
+ // TODO: This is so ugly
+ switch (typeInfo.type_name.at(0))
+ {
+ case '7':
+ return TIVClass;
+ case '9':
+ return TIVSIClass;
+ case '1':
+ if (typeInfo.type_name.at(1) == '4')
+ return TIVVMIClass;
+ default:
+ return std::nullopt;
+ }
+ }
+
+ return std::nullopt;
+}
+
+
+std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
+{
+ // TODO: You cant get subobject offsets from rtti, its stored above this ptr in vtable.
+ // Get object as type info then check to see if it's valid.
+ auto typeInfoVariant = ReadTypeInfoVariant(m_view, objectAddr);
+ if (!typeInfoVariant.has_value())
+ return std::nullopt;
+
+ auto typeInfo = TypeInfo(m_view, objectAddr);
+ auto className = DemangleNameGNU3(m_view, allowMangledClassNames, typeInfo.type_name);
+ if (!className.has_value())
+ return std::nullopt;
+ auto classInfo = ClassInfo{className.value()};
+
+ // TODO: className starts with 7, 9, 14
+ // 7 == class_type
+ // 9 == si_class_type
+ // 14 == vmi_class_type
+
+ auto typeInfoName = fmt::format("_typeinfo_for_{}", classInfo.className);
+ m_view->DefineAutoSymbol(new Symbol{DataSymbol, typeInfoName, objectAddr});
+
+ if (typeInfoVariant == TIVSIClass)
+ {
+ // Read the base class.
+ auto siClassTypeInfo = SIClassTypeInfo(m_view, objectAddr);
+ auto subTypeInfoVariant = ReadTypeInfoVariant(m_view, siClassTypeInfo.base_type);
+ if (!subTypeInfoVariant.has_value())
+ return std::nullopt;
+ auto subTypeInfo = TypeInfo(m_view, siClassTypeInfo.base_type);
+ // Demangle base class name and set
+ auto baseClassName = DemangleNameGNU3(m_view, allowMangledClassNames, subTypeInfo.type_name);
+ if (!baseClassName.has_value())
+ {
+ m_logger->LogWarn("Skipping base class with mangled name %llx", siClassTypeInfo.base_type);
+ return std::nullopt;
+ }
+ classInfo.baseClassName = baseClassName;
+ m_view->DefineDataVariable(objectAddr, Confidence(SIClassTypeInfoType(m_view), 255));
+ }
+ else if (typeInfoVariant == TIVVMIClass)
+ {
+ // TODO: Read multiple base classes.
+ auto vmiClassTypeInfo = VMIClassTypeInfo(m_view, objectAddr);
+ m_view->DefineDataVariable(objectAddr, Confidence(VMIClassTypeInfoType(m_view, vmiClassTypeInfo.base_count), 255));
+ }
+ else
+ {
+ // auto classTypeInfo = ClassTypeInfo(m_view, objectAddr);
+ m_view->DefineDataVariable(objectAddr, Confidence(ClassTypeInfoType(m_view), 255));
+ }
+
+ return classInfo;
+}
+
+
+ItaniumRTTIProcessor::ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool useMangled, bool checkRData, bool vftSweep) : m_view(view)
+{
+ m_logger = new Logger("Itanium RTTI");
+ allowMangledClassNames = useMangled;
+ checkWritableRData = checkRData;
+ m_classInfo = {};
+ virtualFunctionTableSweep = vftSweep;
+
+ auto metadata = view->QueryMetadata(VIEW_METADATA_RTTI);
+ if (metadata != nullptr)
+ {
+ // TODO: This will pull in microsoft RTTI, which is really weird behavior possibly.
+ // Load in metadata to the processor.
+ // DeserializedMetadata(metadata);
+ }
+}
+
+
+void ItaniumRTTIProcessor::ProcessRTTI()
+{
+ auto start_time = std::chrono::high_resolution_clock::now();
+ auto addrSize = m_view->GetAddressSize();
+ // TODO: This probably needs to change
+ uint64_t maxTypeInfoSize = 0x10;
+
+ auto scan = [&](const Ref<Section> &section) {
+ for (uint64_t currAddr = section->GetStart(); currAddr <= section->GetEnd() - maxTypeInfoSize; currAddr += addrSize)
+ {
+ if (auto classInfo = ProcessRTTI(currAddr))
+ m_classInfo[currAddr] = classInfo.value();
+ }
+ };
+
+ // Scan data sections for rtti.
+ for (const Ref<Section> &section: m_view->GetSections())
+ {
+ if (section->GetSemantics() == ReadOnlyDataSectionSemantics)
+ {
+ m_logger->LogDebug("Attempting to find RTTI in section %llx", section->GetStart());
+ scan(section);
+ }
+ }
+
+ auto end_time = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double> elapsed_time = end_time - start_time;
+ m_logger->LogInfo("ProcessRTTI took %f seconds", elapsed_time.count());
+} \ No newline at end of file
diff --git a/plugins/rtti/itanium.h b/plugins/rtti/itanium.h
new file mode 100644
index 00000000..01a765bb
--- /dev/null
+++ b/plugins/rtti/itanium.h
@@ -0,0 +1,136 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+#include "rtti.h"
+
+namespace BinaryNinja::RTTI::Itanium {
+ enum TypeInfoVariant
+ {
+ TIVFundamental,
+ TIVArray,
+ TIVFunction,
+ TIVEnum,
+ TIVClass,
+ TIVSIClass,
+ TIVVMIClass,
+ TIVBasePointer,
+ TIVPointer,
+ TIVPointerToMember,
+ };
+
+ struct TypeInfo
+ {
+ // This might also be zero, and also this is at -1 offset.
+ uint64_t base;
+ std::string type_name;
+
+ TypeInfo(BinaryView *view, uint64_t address);
+ };
+
+ struct FundamentalTypeInfo : TypeInfo {};
+
+ struct ArrayTypeInfo : TypeInfo {};
+
+ struct FunctionTypeInfo : TypeInfo {};
+
+ struct EnumTypeInfo : TypeInfo {};
+
+ struct ClassTypeInfo : TypeInfo
+ {
+ ClassTypeInfo(BinaryView *view, uint64_t uint64) : TypeInfo(view, uint64) {}
+ };
+
+ struct SIClassTypeInfo : ClassTypeInfo
+ {
+ uint64_t base_type;
+
+ SIClassTypeInfo(BinaryView *view, uint64_t address);
+ };
+
+ enum OffsetFlagsMasks
+ {
+ virtual_mask = 0x1,
+ public_mask = 0x2,
+ offset_shift = 8
+ };
+
+ struct BaseClassTypeInfo
+ {
+ uint64_t base_type;
+ uint64_t offset_flags;
+ OffsetFlagsMasks offset_flags_masks;
+
+ BaseClassTypeInfo(BinaryView *view, uint64_t address);
+ };
+
+ struct VMIClassTypeInfo : ClassTypeInfo
+ {
+ uint64_t flags;
+ uint64_t base_count;
+ std::vector<BaseClassTypeInfo> base_info;
+
+ VMIClassTypeInfo(BinaryView *view, uint64_t address);
+ };
+
+ enum BasePointerMasks
+ {
+ // `pointee` type has const qualifier
+ const_mask = 0x1,
+ // `pointee` type has volatile qualifier
+ volatile_mask = 0x2,
+ // `pointee` type has restrict qualifier
+ restrict_mask = 0x4,
+ // `pointee` type is incomplete
+ incomplete_mask = 0x8,
+ // class containing `pointee` is incomplete (in pointer to member)
+ incomplete_class_mask = 0x10,
+ // `pointee` type is function type without the transaction-safe indication
+ transaction_safe_mask = 0x20,
+ // `pointee` type is function type without the exception specification
+ noexcept_mask = 0x40
+ };
+
+ struct BasePointerTypeInfo : TypeInfo
+ {
+ uint64_t flags;
+ uint64_t pointee;
+ BasePointerMasks masks;
+
+ BasePointerTypeInfo(BinaryView *view, uint64_t address);
+ };
+
+ struct PointerTypeInfo : BasePointerTypeInfo {};
+
+ struct PointerToMemberTypeInfo : BasePointerTypeInfo
+ {
+ uint64_t context;
+
+ PointerToMemberTypeInfo(BinaryView *view, uint64_t address);
+ };
+
+ class ItaniumRTTIProcessor
+ {
+ Ref<BinaryView> m_view;
+ Ref<Logger> m_logger;
+ bool allowMangledClassNames;
+ bool checkWritableRData;
+ bool virtualFunctionTableSweep;
+
+ std::map<uint64_t, ClassInfo> m_classInfo;
+
+ void DeserializedMetadata(const Ref<Metadata> &metadata);
+
+ std::optional<VirtualFunctionTableInfo> ProcessVTT(uint64_t vttAddr, const ClassInfo &classInfo);
+
+ public:
+ ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool useMangled = true, bool checkRData = true, bool vttSweep = true);
+
+ Ref<Metadata> SerializedMetadata();
+
+ void ProcessRTTI();
+
+ std::optional<ClassInfo> ProcessRTTI(uint64_t objectAddr);
+
+ void ProcessVTT();
+ };
+} \ No newline at end of file
diff --git a/plugins/rtti/microsoft.cpp b/plugins/rtti/microsoft.cpp
new file mode 100644
index 00000000..07a33d61
--- /dev/null
+++ b/plugins/rtti/microsoft.cpp
@@ -0,0 +1,875 @@
+#include "rtti.h"
+
+using namespace BinaryNinja;
+
+constexpr int COL_SIG_REV0 = 0;
+constexpr int COL_SIG_REV1 = 1;
+constexpr int RTTI_CONFIDENCE = 100;
+
+constexpr int BCD_HASPCHD = 0x40;
+
+ClassHierarchyDescriptor::ClassHierarchyDescriptor(BinaryView *view, uint64_t address)
+{
+ BinaryReader reader = BinaryReader(view);
+ reader.Seek(address);
+ signature = reader.Read32();
+ attributes = reader.Read32();
+ numBaseClasses = reader.Read32();
+ pBaseClassArray = static_cast<int32_t>(reader.Read32());
+}
+
+
+BaseClassDescriptor::BaseClassDescriptor(BinaryView *view, uint64_t address)
+{
+ BinaryReader reader = BinaryReader(view);
+ reader.Seek(address);
+ pTypeDescriptor = static_cast<int32_t>(reader.Read32());
+ numContainedBases = reader.Read32();
+ where_mdisp = static_cast<int32_t>(reader.Read32());
+ where_pdisp = static_cast<int32_t>(reader.Read32());
+ where_vdisp = static_cast<int32_t>(reader.Read32());
+ attributes = reader.Read32();
+ pClassHierarchyDescriptor = static_cast<int32_t>(reader.Read32());
+}
+
+
+BaseClassArray::BaseClassArray(BinaryView *view, uint64_t address, uint32_t length) : length(length)
+{
+ BinaryReader reader = BinaryReader(view);
+ reader.Seek(address);
+ descriptors = {};
+ for (size_t i = 0; i < length; i++)
+ descriptors.emplace_back(reader.Read32());
+}
+
+
+TypeDescriptor::TypeDescriptor(BinaryView *view, uint64_t address)
+{
+ BinaryReader reader = BinaryReader(view);
+ reader.Seek(address);
+ pVFTable = reader.ReadPointer();
+ spare = reader.ReadPointer();
+ name = reader.ReadCString(512);
+}
+
+
+CompleteObjectLocator::CompleteObjectLocator(BinaryView *view, uint64_t address)
+{
+ BinaryReader reader = BinaryReader(view);
+ reader.Seek(address);
+ signature = reader.Read32();
+ offset = reader.Read32();
+ cdOffset = reader.Read32();
+ pTypeDescriptor = static_cast<int32_t>(reader.Read32());
+ pClassHierarchyDescriptor = static_cast<int32_t>(reader.Read32());
+ if (signature == COL_SIG_REV1)
+ {
+ pSelf = static_cast<int32_t>(reader.Read32());
+ }
+ else
+ {
+ pSelf = 0;
+ }
+}
+
+
+std::optional<CompleteObjectLocator> ReadCompleteObjectorLocator(BinaryView *view, uint64_t address)
+{
+ auto coLocator = CompleteObjectLocator(view, address);
+ uint64_t startAddr = view->GetOriginalImageBase();
+
+ auto outsideSection = [&](uint64_t addr) {
+ return view->GetSectionsAt(addr).empty();
+ };
+
+ if (coLocator.signature > 1)
+ return std::nullopt;
+
+ if (coLocator.signature == COL_SIG_REV1)
+ {
+ if (coLocator.pSelf + startAddr != address)
+ return std::nullopt;
+
+ // Relative addrs
+ if (outsideSection(coLocator.pTypeDescriptor + startAddr))
+ return std::nullopt;
+
+ if (outsideSection(coLocator.pClassHierarchyDescriptor + startAddr))
+ return std::nullopt;
+ }
+ else
+ {
+ // Absolute addrs
+ if (outsideSection(coLocator.pTypeDescriptor))
+ return std::nullopt;
+
+ if (outsideSection(coLocator.pClassHierarchyDescriptor))
+ return std::nullopt;
+ }
+
+ return coLocator;
+}
+
+
+Ref<Type> GetPMDType(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("PMD"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Type> intType = Type::IntegerType(4, true);
+
+ StructureBuilder pmdBuilder;
+ pmdBuilder.AddMember(intType, "mdisp");
+ pmdBuilder.AddMember(intType, "pdisp");
+ pmdBuilder.AddMember(intType, "vdisp");
+
+ view->DefineType(typeId, QualifiedName("_PMD"), TypeBuilder::StructureType(pmdBuilder.Finalize()).Finalize());
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> ClassHierarchyDescriptorType(BinaryView *view, BNPointerBaseType ptrBaseTy);
+
+Ref<Type> BaseClassDescriptorType(BinaryView *view, BNPointerBaseType ptrBaseTy)
+{
+ auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("RTTIBaseClassDescriptor"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+ Ref<Type> uintType = Type::IntegerType(4, false);
+
+ StructureBuilder baseClassDescriptorBuilder;
+ // Would require creating a new type for every type descriptor length. Instead just use void*
+ Ref<Type> pTypeDescType = TypeBuilder::PointerType(4, Type::VoidType())
+ .SetPointerBase(ptrBaseTy, 0)
+ .Finalize();
+ baseClassDescriptorBuilder.AddMember(pTypeDescType, "pTypeDescriptor");
+ baseClassDescriptorBuilder.AddMember(uintType, "numContainedBases");
+ baseClassDescriptorBuilder.AddMember(GetPMDType(view), "where");
+ Ref<Enumeration> attrEnum = EnumerationBuilder()
+ .AddMemberWithValue("BCD_NOT_VISIBLE", 0x01)
+ .AddMemberWithValue("BCD_AMBIGUOUS", 0x02)
+ .AddMemberWithValue("BCD_PRIVORPROTBASE", 0x04)
+ .AddMemberWithValue("BCD_PRIVORPROTINCOMPOBJ", 0x08)
+ .AddMemberWithValue("BCD_VBOFCONTOBJ", 0x10)
+ .AddMemberWithValue("BCD_NONPOLYMORPHIC", 0x20)
+ .AddMemberWithValue("BCD_HASPCHD", 0x40)
+ .Finalize();
+ Ref<Type> attrType = Type::EnumerationType(arch, attrEnum, 4);
+ baseClassDescriptorBuilder.AddMember(attrType, "attributes");
+ Ref<Type> pClassDescType = TypeBuilder::PointerType(4, ClassHierarchyDescriptorType(view, ptrBaseTy))
+ .SetPointerBase(ptrBaseTy, 0)
+ .Finalize();
+ baseClassDescriptorBuilder.AddMember(pClassDescType, "pClassDescriptor");
+
+ view->DefineType(typeId, QualifiedName("_RTTIBaseClassDescriptor"),
+ TypeBuilder::StructureType(baseClassDescriptorBuilder.Finalize()).Finalize());
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> BaseClassArrayType(BinaryView *view, const uint64_t length, BNPointerBaseType ptrBaseTy)
+{
+ StructureBuilder baseClassArrayBuilder;
+ Ref<Type> pBaseClassDescType = TypeBuilder::PointerType(4, BaseClassDescriptorType(view, ptrBaseTy))
+ .SetPointerBase(ptrBaseTy, 0)
+ .Finalize();
+ baseClassArrayBuilder.AddMember(
+ Type::ArrayType(pBaseClassDescType, length), "arrayOfBaseClassDescriptors");
+ return TypeBuilder::StructureType(baseClassArrayBuilder.Finalize()).Finalize();
+}
+
+
+Ref<Type> ClassHierarchyDescriptorType(BinaryView *view, BNPointerBaseType ptrBaseTy)
+{
+ auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("RTTIClassHierarchyDescriptor"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+ Ref<Type> uintType = Type::IntegerType(4, false);
+
+ StructureBuilder classHierarchyDescriptorBuilder;
+ classHierarchyDescriptorBuilder.AddMember(uintType, "signature");
+ Ref<Enumeration> attrEnum = EnumerationBuilder()
+ .AddMemberWithValue("CHD_MULTINH", 0x01)
+ .AddMemberWithValue("CHD_VIRTINH", 0x02)
+ .AddMemberWithValue("CHD_AMBIGUOUS", 0x04)
+ .Finalize();
+ Ref<Type> attrType = Type::EnumerationType(arch, attrEnum, 4);
+ classHierarchyDescriptorBuilder.AddMember(attrType, "attributes");
+ classHierarchyDescriptorBuilder.AddMember(uintType, "numBaseClasses");
+ Ref<Type> pBaseClassArrayType = TypeBuilder::PointerType(4, Type::VoidType())
+ .SetPointerBase(ptrBaseTy, 0)
+ .Finalize();
+ classHierarchyDescriptorBuilder.AddMember(pBaseClassArrayType, "pBaseClassArray");
+
+ view->DefineType(typeId, QualifiedName("_RTTIClassHierarchyDescriptor"),
+ TypeBuilder::StructureType(classHierarchyDescriptorBuilder.Finalize()).Finalize());
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> CompleteObjectLocator64Type(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("RTTICompleteObjectLocator64"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+ Ref<Type> uintType = Type::IntegerType(4, false);
+
+ StructureBuilder completeObjectLocatorBuilder;
+ Ref<Enumeration> sigEnum = EnumerationBuilder()
+ .AddMemberWithValue("COL_SIG_REV0", 0)
+ .AddMemberWithValue("COL_SIG_REV1", 1)
+ .Finalize();
+ Ref<Type> sigType = Type::EnumerationType(arch, sigEnum, 4);
+ completeObjectLocatorBuilder.AddMember(sigType, "signature");
+ completeObjectLocatorBuilder.AddMember(uintType, "offset");
+ completeObjectLocatorBuilder.AddMember(uintType, "cdOffset");
+ Ref<Type> pTypeDescType = TypeBuilder::PointerType(4, Type::VoidType())
+ .SetPointerBase(RelativeToBinaryStartPointerBaseType, 0)
+ .Finalize();
+ completeObjectLocatorBuilder.AddMember(pTypeDescType, "pTypeDescriptor");
+ Ref<Type> pClassHierarchyDescType = TypeBuilder::PointerType(
+ 4, ClassHierarchyDescriptorType(view, RelativeToBinaryStartPointerBaseType))
+ .SetPointerBase(RelativeToBinaryStartPointerBaseType, 0)
+ .Finalize();
+ completeObjectLocatorBuilder.AddMember(pClassHierarchyDescType, "pClassHierarchyDescriptor");
+ Ref<Type> pSelfType = TypeBuilder::PointerType(4, Type::NamedType(view, typeId))
+ .SetPointerBase(RelativeToBinaryStartPointerBaseType, 0)
+ .Finalize();
+ completeObjectLocatorBuilder.AddMember(pSelfType, "pSelf");
+
+ view->DefineType(typeId, QualifiedName("_RTTICompleteObjectLocator"),
+ TypeBuilder::StructureType(completeObjectLocatorBuilder.Finalize()).Finalize());
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> CompleteObjectLocator32Type(BinaryView *view)
+{
+ auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("RTTICompleteObjectLocator32"));
+ Ref<Type> typeCache = view->GetTypeById(typeId);
+
+ if (typeCache == nullptr)
+ {
+ Ref<Architecture> arch = view->GetDefaultArchitecture();
+ Ref<Type> uintType = Type::IntegerType(4, false);
+
+ StructureBuilder completeObjectLocatorBuilder;
+ Ref<Enumeration> sigEnum = EnumerationBuilder()
+ .AddMemberWithValue("COL_SIG_REV0", 0)
+ .AddMemberWithValue("COL_SIG_REV1", 1)
+ .Finalize();
+ Ref<Type> sigType = Type::EnumerationType(arch, sigEnum, 4);
+ completeObjectLocatorBuilder.AddMember(sigType, "signature");
+ completeObjectLocatorBuilder.AddMember(uintType, "offset");
+ completeObjectLocatorBuilder.AddMember(uintType, "cdOffset");
+ Ref<Type> pTypeDescType = TypeBuilder::PointerType(4, Type::VoidType())
+ .Finalize();
+ completeObjectLocatorBuilder.AddMember(pTypeDescType, "pTypeDescriptor");
+ Ref<Type> pClassHierarchyDescType = TypeBuilder::PointerType(
+ 4, ClassHierarchyDescriptorType(view, AbsolutePointerBaseType))
+ .Finalize();
+ completeObjectLocatorBuilder.AddMember(pClassHierarchyDescType, "pClassHierarchyDescriptor");
+
+ view->DefineType(typeId, QualifiedName("_RTTICompleteObjectLocator"),
+ TypeBuilder::StructureType(completeObjectLocatorBuilder.Finalize()).Finalize());
+
+ typeCache = view->GetTypeById(typeId);
+ }
+
+ return typeCache;
+}
+
+
+Ref<Type> TypeDescriptorType(BinaryView *view, uint64_t length)
+{
+ size_t addrSize = view->GetAddressSize();
+ StructureBuilder typeDescriptorBuilder;
+ typeDescriptorBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType(), true), "pVFTable");
+ typeDescriptorBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "spare");
+ // Char array needs to be individually resized.
+ typeDescriptorBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true, "char"), length), "name");
+ return TypeBuilder::StructureType(typeDescriptorBuilder.Finalize()).Finalize();
+}
+
+
+Ref<Metadata> ClassInfo::SerializedMetadata()
+{
+ std::map<std::string, Ref<Metadata> > classInfoMeta;
+ classInfoMeta["className"] = new Metadata(className);
+ if (baseClassName.has_value())
+ classInfoMeta["baseClassName"] = new Metadata(baseClassName.value());
+ if (classOffset.has_value())
+ classInfoMeta["classOffset"] = new Metadata(classOffset.value());
+ if (vft.has_value())
+ classInfoMeta["vft"] = vft->SerializedMetadata();
+ // NOTE: We omit baseVft as it can be resolved manually and just bloats the size.
+ return new Metadata(classInfoMeta);
+}
+
+
+ClassInfo ClassInfo::DeserializedMetadata(const Ref<Metadata> &metadata)
+{
+ std::map<std::string, Ref<Metadata> > classInfoMeta = metadata->GetKeyValueStore();
+ ClassInfo info = {classInfoMeta["className"]->GetString()};
+ if (classInfoMeta.find("baseClassName") != classInfoMeta.end())
+ info.baseClassName = classInfoMeta["baseClassName"]->GetString();
+ if (classInfoMeta.find("classOffset") != classInfoMeta.end())
+ info.classOffset = classInfoMeta["classOffset"]->GetUnsignedInteger();
+ if (classInfoMeta.find("vft") != classInfoMeta.end())
+ info.vft = VirtualFunctionTableInfo::DeserializedMetadata(classInfoMeta["vft"]);
+ return info;
+}
+
+
+Ref<Metadata> VirtualFunctionTableInfo::SerializedMetadata()
+{
+ std::vector<Ref<Metadata> > funcsMeta;
+ funcsMeta.reserve(virtualFunctions.size());
+ for (auto &vFunc: virtualFunctions)
+ funcsMeta.emplace_back(vFunc.SerializedMetadata());
+ std::map<std::string, Ref<Metadata> > vftMeta;
+ vftMeta["address"] = new Metadata(address);
+ vftMeta["functions"] = new Metadata(funcsMeta);
+ return new Metadata(vftMeta);
+}
+
+
+VirtualFunctionTableInfo VirtualFunctionTableInfo::DeserializedMetadata(const Ref<Metadata> &metadata)
+{
+ std::map<std::string, Ref<Metadata> > vftMeta = metadata->GetKeyValueStore();
+ VirtualFunctionTableInfo vftInfo = {vftMeta["address"]->GetUnsignedInteger()};
+ if (vftMeta.find("functions") != vftMeta.end())
+ {
+ for (auto &entry: vftMeta["functions"]->GetArray())
+ vftInfo.virtualFunctions.emplace_back(VirtualFunctionInfo::DeserializedMetadata(entry));
+ }
+ return vftInfo;
+}
+
+
+Ref<Metadata> VirtualFunctionInfo::SerializedMetadata()
+{
+ std::map<std::string, Ref<Metadata> > vFuncMeta;
+ vFuncMeta["address"] = new Metadata(funcAddr);
+ return new Metadata(vFuncMeta);
+}
+
+
+VirtualFunctionInfo VirtualFunctionInfo::DeserializedMetadata(const Ref<Metadata> &metadata)
+{
+ std::map<std::string, Ref<Metadata> > vFuncMeta = metadata->GetKeyValueStore();
+ VirtualFunctionInfo vFuncInfo = {vFuncMeta["address"]->GetUnsignedInteger()};
+ return vFuncInfo;
+}
+
+
+Ref<Metadata> MicrosoftRTTIProcessor::SerializedMetadata()
+{
+ std::map<std::string, Ref<Metadata> > classesMeta;
+ for (auto &[coLocatorAddr, classInfo]: m_classInfo)
+ {
+ auto addrStr = std::to_string(coLocatorAddr);
+ classesMeta[addrStr] = classInfo.SerializedMetadata();
+ }
+
+ std::map<std::string, Ref<Metadata> > msvcMeta;
+ msvcMeta["classes"] = new Metadata(classesMeta);
+ return new Metadata(msvcMeta);
+}
+
+
+void MicrosoftRTTIProcessor::DeserializedMetadata(const Ref<Metadata> &metadata)
+{
+ std::map<std::string, Ref<Metadata> > msvcMeta = metadata->GetKeyValueStore();
+ if (msvcMeta.find("classes") != msvcMeta.end())
+ {
+ for (auto &[coLocatorAddrStr, classInfoMeta]: msvcMeta["classes"]->GetKeyValueStore())
+ {
+ uint64_t coLocatorAddr = std::stoull(coLocatorAddrStr);
+ m_classInfo[coLocatorAddr] = ClassInfo::DeserializedMetadata(classInfoMeta);
+ }
+ }
+}
+
+
+std::optional<std::string> MicrosoftRTTIProcessor::DemangleName(const std::string &mangledName)
+{
+ QualifiedName demangledName = {};
+ Ref<Type> outType = {};
+ if (!DemangleMS(m_view->GetDefaultArchitecture(), mangledName, outType, demangledName, true))
+ {
+ // Try to use LLVM demangler.
+ if (!DemangleLLVM(mangledName, demangledName, true))
+ return allowMangledClassNames ? std::optional(mangledName) : std::nullopt;
+ auto demangledNameStr = demangledName.GetString();
+ size_t beginFind = demangledNameStr.find_first_of(' ');
+ if (beginFind != std::string::npos)
+ demangledNameStr.erase(0, beginFind + 1);
+ size_t endFind = demangledNameStr.find(" `RTTI Type Descriptor Name'");
+ if (endFind != std::string::npos)
+ demangledNameStr.erase(endFind, demangledNameStr.length());
+ return demangledNameStr;
+ }
+ return demangledName.GetString();
+}
+
+
+std::optional<ClassInfo> MicrosoftRTTIProcessor::ProcessRTTI(uint64_t coLocatorAddr)
+{
+ // Get complete object locator then check to see if its valid.
+ auto coLocator = ReadCompleteObjectorLocator(m_view, coLocatorAddr);
+ if (!coLocator.has_value())
+ return std::nullopt;
+
+ auto startAddr = m_view->GetOriginalImageBase();
+ auto resolveAddr = [&](const uint64_t relAddr) {
+ return coLocator->signature == COL_SIG_REV1 ? startAddr + relAddr : relAddr;
+ };
+
+ auto ptrBaseTy = coLocator->signature ? RelativeToBinaryStartPointerBaseType : AbsolutePointerBaseType;
+
+ auto defineClassHierarchyDesc = [&](const uint64_t classHierarchyDescAddr, ClassInfo& classInfo, std::optional<CompleteObjectLocator> coLocator) {
+ auto classHierarchyDesc = ClassHierarchyDescriptor(m_view, classHierarchyDescAddr);
+ auto classHierarchyDescName = fmt::format("{}::`RTTI Class Hierarchy Descriptor'", classInfo.className);
+ m_view->DefineAutoSymbol(new Symbol{DataSymbol, classHierarchyDescName, classHierarchyDescAddr});
+ m_view->DefineDataVariable(classHierarchyDescAddr,
+ Confidence(ClassHierarchyDescriptorType(m_view, ptrBaseTy), RTTI_CONFIDENCE));
+
+ auto baseClassArrayAddr = resolveAddr(classHierarchyDesc.pBaseClassArray);
+ auto baseClassArray = BaseClassArray(m_view, baseClassArrayAddr, classHierarchyDesc.numBaseClasses);
+ auto baseClassArrayName = fmt::format("{}::`RTTI Base Class Array'", classInfo.className);
+ m_view->DefineAutoSymbol(new Symbol{DataSymbol, baseClassArrayName, baseClassArrayAddr});
+ m_view->DefineDataVariable(baseClassArrayAddr,
+ Confidence(BaseClassArrayType(m_view, baseClassArray.length, ptrBaseTy),
+ RTTI_CONFIDENCE));
+
+ std::map<uint64_t, ClassInfo> baseClasses = {};
+ for (auto pBaseClassDescAddr: baseClassArray.descriptors)
+ {
+ auto baseClassDescAddr = resolveAddr(pBaseClassDescAddr);
+ auto baseClassDesc = BaseClassDescriptor(m_view, baseClassDescAddr);
+
+ auto baseClassTypeDescAddr = resolveAddr(baseClassDesc.pTypeDescriptor);
+ auto baseClassTypeDesc = TypeDescriptor(m_view, baseClassTypeDescAddr);
+ auto baseClassName = DemangleName(baseClassTypeDesc.name);
+ if (!baseClassName.has_value())
+ {
+ m_logger->LogWarn("Skipping BaseClassDescriptor with mangled name %llx", baseClassTypeDescAddr);
+ continue;
+ }
+
+ // TODO: we probably want to maintain this state
+ auto baseClassInfo = ClassInfo{baseClassName.value()};
+
+ if (coLocator.has_value())
+ {
+ if (baseClassDesc.where_mdisp == coLocator->offset && !classInfo.baseClassName.has_value() && classInfo.className != baseClassInfo.className)
+ classInfo.baseClassName = baseClassInfo.className;
+ }
+
+ auto baseClassDescName = fmt::format("{}::`RTTI Base Class Descriptor at ({},{},{},{})", baseClassInfo.className,
+ baseClassDesc.where_mdisp, baseClassDesc.where_pdisp,
+ baseClassDesc.where_vdisp, baseClassDesc.attributes);
+ m_view->DefineAutoSymbol(new Symbol{DataSymbol, baseClassDescName, baseClassDescAddr});
+ m_view->DefineDataVariable(baseClassDescAddr,
+ Confidence(BaseClassDescriptorType(m_view, ptrBaseTy), RTTI_CONFIDENCE));
+
+ auto baseClassTypeDescSymName = fmt::format("class {} `RTTI Type Descriptor'", baseClassInfo.className);
+ m_view->DefineAutoSymbol(new Symbol{DataSymbol, baseClassTypeDescSymName, baseClassTypeDescAddr});
+ m_view->DefineDataVariable(baseClassTypeDescAddr,
+ Confidence(TypeDescriptorType(m_view, baseClassTypeDesc.name.length()), RTTI_CONFIDENCE));
+
+ if (baseClassDesc.attributes & BCD_HASPCHD) {
+ auto classHierarchyDescAddr = resolveAddr(baseClassDesc.pClassHierarchyDescriptor);
+ baseClasses[classHierarchyDescAddr] = baseClassInfo;
+ }
+ }
+
+ return baseClasses;
+ };
+
+ // Get type descriptor then check to see if the class name was demangled.
+ auto typeDescAddr = resolveAddr(coLocator->pTypeDescriptor);
+ auto typeDesc = TypeDescriptor(m_view, typeDescAddr);
+ auto className = DemangleName(typeDesc.name);
+ if (!className.has_value())
+ return std::nullopt;
+
+ // If the className is empty we will change it to the address, this is to fix type clobbering.
+ if (className->empty())
+ {
+ if (!allowAnonymousClassNames)
+ {
+ m_logger->LogDebug("Skipping CompleteObjectorLocator with anonymous name %llx", coLocatorAddr);
+ return std::nullopt;
+ }
+ className = fmt::format("ANONYMOUS_{:#x}", coLocatorAddr);
+ }
+
+ auto classInfo = ClassInfo{className.value()};
+ if (coLocator->offset > 0)
+ classInfo.classOffset = coLocator->offset;
+
+ auto typeDescSymName = fmt::format("class {} `RTTI Type Descriptor'", classInfo.className);
+ m_view->DefineAutoSymbol(new Symbol{DataSymbol, typeDescSymName, typeDescAddr});
+ m_view->DefineDataVariable(typeDescAddr,
+ Confidence(TypeDescriptorType(m_view, typeDesc.name.length()), RTTI_CONFIDENCE));
+
+ auto classHierarchyDescAddr = resolveAddr(coLocator->pClassHierarchyDescriptor);
+ auto baseClasses = defineClassHierarchyDesc(classHierarchyDescAddr, classInfo, coLocator);
+ m_visitedClassHierarchyDescAddrs.insert(classHierarchyDescAddr);
+ while (baseClasses.size() > 0)
+ {
+ std::map<uint64_t, ClassInfo> newBaseClasses = {};
+ for (auto& [baseClassHierarchyDescAddr, baseClassInfo] : baseClasses)
+ {
+ if (m_visitedClassHierarchyDescAddrs.find(baseClassHierarchyDescAddr) != m_visitedClassHierarchyDescAddrs.end())
+ continue;
+
+ newBaseClasses.merge(defineClassHierarchyDesc(baseClassHierarchyDescAddr, baseClassInfo, std::nullopt));
+ m_visitedClassHierarchyDescAddrs.insert(baseClassHierarchyDescAddr);
+ }
+
+ baseClasses = newBaseClasses;
+ }
+
+ auto coLocatorName = fmt::format("{}::`RTTI Complete Object Locator'", className.value());
+ if (classInfo.baseClassName.has_value())
+ coLocatorName += fmt::format("{{for `{}'}}", classInfo.baseClassName.value());
+ m_view->DefineAutoSymbol(new Symbol{DataSymbol, coLocatorName, coLocatorAddr});
+ if (coLocator->signature == COL_SIG_REV1)
+ m_view->DefineDataVariable(coLocatorAddr, Confidence(CompleteObjectLocator64Type(m_view), RTTI_CONFIDENCE));
+ else
+ m_view->DefineDataVariable(coLocatorAddr, Confidence(CompleteObjectLocator32Type(m_view), RTTI_CONFIDENCE));
+
+ return classInfo;
+}
+
+
+std::optional<VirtualFunctionTableInfo> MicrosoftRTTIProcessor::ProcessVFT(uint64_t vftAddr, const ClassInfo &classInfo)
+{
+ VirtualFunctionTableInfo vftInfo = {vftAddr};
+ // Gather all virtual functions
+ BinaryReader reader = BinaryReader(m_view);
+ reader.Seek(vftAddr);
+ // Virtual functions and the analysis object of it, if it exists.
+ std::vector<std::pair<uint64_t, std::optional<Ref<Function>>>> virtualFunctions = {};
+ while (true)
+ {
+ uint64_t vFuncAddr = reader.ReadPointer();
+ auto funcs = m_view->GetAnalysisFunctionsForAddress(vFuncAddr);
+ if (funcs.empty())
+ {
+ Ref<Segment> segment = m_view->GetSegmentAt(vFuncAddr);
+ if (segment == nullptr || !(segment->GetFlags() & (SegmentExecutable | SegmentDenyWrite)))
+ {
+ // Last CompleteObjectLocator or hit the next CompleteObjectLocator
+ break;
+ }
+ // TODO: Is likely a function check here?
+ m_logger->LogDebug("Discovered function from virtual function table... %llx", vFuncAddr);
+ auto vFunc = m_view->AddFunctionForAnalysis(m_view->GetDefaultPlatform(), vFuncAddr, true);
+ virtualFunctions.emplace_back(vFuncAddr, vFunc ? std::optional(vFunc) : std::nullopt);
+ }
+ else
+ {
+ // Only ever add one function.
+ virtualFunctions.emplace_back(vFuncAddr, funcs.front());
+ }
+ }
+
+ if (virtualFunctions.empty())
+ {
+ m_logger->LogDebug("Skipping empty virtual function table... %llx", vftAddr);
+ return std::nullopt;
+ }
+
+ for (auto &[vFuncAddr, _]: virtualFunctions)
+ vftInfo.virtualFunctions.emplace_back(VirtualFunctionInfo{vFuncAddr});
+
+ // Create virtual function table type
+ auto vftTypeName = fmt::format("{}::VTable", classInfo.className);
+ if (classInfo.baseClassName.has_value())
+ {
+ vftTypeName = fmt::format("{}::{}", classInfo.baseClassName.value(), vftTypeName);
+ // TODO: What is the correct form for the name?
+ }
+ // TODO: Hack the debug type id is used here to allow the PDB type (debug info) to overwrite the RTTI vtable type.
+ auto typeId = Type::GenerateAutoDebugTypeId(vftTypeName);
+ Ref<Type> vftType = m_view->GetTypeById(typeId);
+
+ if (vftType == nullptr)
+ {
+ size_t addrSize = m_view->GetAddressSize();
+ StructureBuilder vftBuilder = {};
+ vftBuilder.SetPropagateDataVariableReferences(true);
+ size_t vFuncIdx = 0;
+
+ // Until https://github.com/Vector35/binaryninja-api/issues/5982 is fixed
+ auto vftSize = virtualFunctions.size() * addrSize;
+ vftBuilder.SetWidth(vftSize);
+
+ if (auto baseVft = classInfo.baseVft)
+ {
+ if (classInfo.baseVft->virtualFunctions.size() <= virtualFunctions.size())
+ {
+ // Adjust the current vFunc index to the end of the shared vFuncs.
+ vFuncIdx = classInfo.baseVft->virtualFunctions.size();
+ virtualFunctions.erase(virtualFunctions.begin(), virtualFunctions.begin() + vFuncIdx);
+ // We should set the vtable as a base class so that xrefs are propagated (among other things).
+ // NOTE: this means that `this` params will be assumed pre-adjusted, this is normally fine assuming type propagation
+ // NOTE: never occurs on the vft types. Other-wise we need to change this.
+ auto baseVftTypeName = fmt::format("{}::VTable", classInfo.baseClassName.value());
+ NamedTypeReferenceBuilder baseVftNTR;
+ baseVftNTR.SetName(baseVftTypeName);
+ // Width is unresolved here so that we can keep non-base vfuncs un-inherited.
+ auto baseVftSize = vFuncIdx * addrSize;
+ vftBuilder.SetBaseStructures({ BaseStructure(baseVftNTR.Finalize(), 0, baseVftSize) });
+ }
+ else
+ {
+ LogWarn("Skipping adjustments for base VFT with more functions than sub VFT... %llx", vftAddr);
+ }
+ }
+
+ for (auto &&[_, vFunc]: virtualFunctions)
+ {
+ auto vFuncName = fmt::format("vFunc_{}", vFuncIdx);
+ if (vFunc.has_value())
+ {
+ // If we have a better name, use it.
+ auto vFuncObj = vFunc.value();
+ auto vFuncSymName = vFuncObj->GetSymbol()->GetShortName();
+ if (vFuncSymName.compare(0, 4, "sub_") != 0)
+ vFuncName = vFuncObj->GetSymbol()->GetShortName();
+ // MyClass::func -> func
+ std::size_t pos = vFuncName.rfind("::");
+ if (pos != std::string::npos)
+ vFuncName = vFuncName.substr(pos + 2);
+ }
+
+ // NOTE: The analyzed function type might not be available here.
+ auto vFuncOffset = vFuncIdx * addrSize;
+ // We have access to a backing function type, use it, otherwise void!
+ vftBuilder.AddMemberAtOffset(
+ Type::PointerType(addrSize, vFunc.has_value() ? vFunc.value()->GetType() : Type::VoidType(), true), vFuncName, vFuncOffset);
+ vFuncIdx++;
+ }
+ m_view->DefineType(typeId, vftTypeName,
+ Confidence(TypeBuilder::StructureType(vftBuilder.Finalize()).Finalize(), RTTI_CONFIDENCE));
+ }
+
+ auto vftName = fmt::format("{}::`vftable'", classInfo.className);
+ if (classInfo.baseClassName.has_value())
+ vftName += fmt::format("{{for `{}'}}", classInfo.baseClassName.value());
+ m_view->DefineAutoSymbol(new Symbol{DataSymbol, vftName, vftAddr});
+ m_view->DefineDataVariable(vftAddr, Confidence(Type::NamedType(m_view, vftTypeName), RTTI_CONFIDENCE));
+ return vftInfo;
+}
+
+
+MicrosoftRTTIProcessor::MicrosoftRTTIProcessor(const Ref<BinaryView> &view, bool useMangled, bool checkRData, bool vftSweep, bool allowAnonymous) : m_view(view)
+{
+ m_logger = new Logger("Microsoft RTTI");
+ allowMangledClassNames = useMangled;
+ allowAnonymousClassNames = allowAnonymous;
+ checkWritableRData = checkRData;
+ m_classInfo = {};
+ m_visitedClassHierarchyDescAddrs = {};
+ virtualFunctionTableSweep = vftSweep;
+ auto metadata = view->QueryMetadata(VIEW_METADATA_MSVC);
+ if (metadata != nullptr)
+ {
+ // Load in metadata to the processor.
+ DeserializedMetadata(metadata);
+ }
+}
+
+
+void MicrosoftRTTIProcessor::ProcessRTTI()
+{
+ auto start_time = std::chrono::high_resolution_clock::now();
+ uint64_t startAddr = m_view->GetOriginalImageBase();
+ uint64_t endAddr = m_view->GetEnd();
+ BinaryReader optReader = BinaryReader(m_view);
+ auto addrSize = m_view->GetAddressSize();
+
+ auto scan = [&](const Ref<Segment> &segment) {
+ for (uint64_t coLocatorAddr = segment->GetStart(); coLocatorAddr < segment->GetEnd() - 0x18;
+ coLocatorAddr += addrSize)
+ {
+ optReader.Seek(coLocatorAddr);
+ uint32_t sigVal = optReader.Read32();
+ if (sigVal == COL_SIG_REV1)
+ {
+ // Check for self reference
+ optReader.SeekRelative(16);
+ if (optReader.Read32() == coLocatorAddr - startAddr)
+ {
+ if (auto classInfo = ProcessRTTI(coLocatorAddr))
+ m_classInfo[coLocatorAddr] = classInfo.value();
+ }
+ }
+ else if (sigVal == COL_SIG_REV0)
+ {
+ // Check ?AV
+ optReader.SeekRelative(8);
+ uint64_t typeDescNameAddr = optReader.Read32() + 8;
+ if (typeDescNameAddr > startAddr && typeDescNameAddr < endAddr)
+ {
+ // Make sure we do not read across segment boundary.
+ auto typeDescSegment = m_view->GetSegmentAt(typeDescNameAddr);
+ if (typeDescSegment != nullptr && typeDescSegment->GetEnd() - typeDescNameAddr > 4)
+ {
+ optReader.Seek(typeDescNameAddr);
+ auto typeDescNameStart = optReader.ReadString(4);
+ if (typeDescNameStart == ".?AV" || typeDescNameStart == ".?AU" || typeDescNameStart == ".?AW")
+ {
+ if (auto classInfo = ProcessRTTI(coLocatorAddr))
+ m_classInfo[coLocatorAddr] = classInfo.value();
+ }
+ }
+ }
+ }
+ }
+ };
+
+ // Scan data sections for colocators.
+ auto rdataSection = m_view->GetSectionByName(".rdata");
+ for (const Ref<Segment> &segment: m_view->GetSegments())
+ {
+ if (segment->GetFlags() == (SegmentReadable | SegmentContainsData))
+ {
+ m_logger->LogDebug("Attempting to find VirtualFunctionTables in segment %llx", segment->GetStart());
+ scan(segment);
+ }
+ else if (checkWritableRData && rdataSection && rdataSection->GetStart() == segment->GetStart())
+ {
+ m_logger->LogDebug("Attempting to find VirtualFunctionTables in writable rdata segment %llx",
+ segment->GetStart());
+ scan(segment);
+ }
+ }
+
+ auto end_time = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double> elapsed_time = end_time - start_time;
+ m_logger->LogInfo("ProcessRTTI took %f seconds", elapsed_time.count());
+}
+
+
+void MicrosoftRTTIProcessor::ProcessVFT()
+{
+ std::map<uint64_t, uint64_t> vftMap = {};
+ std::map<uint64_t, std::optional<VirtualFunctionTableInfo>> vftFinishedMap = {};
+ auto start_time = std::chrono::high_resolution_clock::now();
+ for (auto &[coLocatorAddr, classInfo]: m_classInfo)
+ {
+ for (auto &ref: m_view->GetDataReferences(coLocatorAddr))
+ {
+ auto vftAddr = ref + m_view->GetAddressSize();
+ vftMap[coLocatorAddr] = vftAddr;
+ }
+ }
+
+ if (virtualFunctionTableSweep)
+ {
+ BinaryReader optReader = BinaryReader(m_view);
+ auto addrSize = m_view->GetAddressSize();
+ auto scan = [&](const Ref<Segment> &segment) {
+ uint64_t startAddr = segment->GetStart();
+ uint64_t endAddr = segment->GetEnd();
+ for (uint64_t vtableAddr = startAddr; vtableAddr < endAddr - 0x18; vtableAddr += addrSize)
+ {
+ optReader.Seek(vtableAddr);
+ uint64_t coLocatorAddr = optReader.ReadPointer();
+ auto coLocator = m_classInfo.find(coLocatorAddr);
+ if (coLocator == m_classInfo.end())
+ continue;
+ // Found a vtable reference to colocator.
+ vftMap[coLocatorAddr] = vtableAddr + addrSize;
+ }
+ };
+
+ // Scan data sections for virtual function tables.
+ auto rdataSection = m_view->GetSectionByName(".rdata");
+ for (const Ref<Segment> &segment: m_view->GetSegments())
+ {
+ if (segment->GetFlags() == (SegmentReadable | SegmentContainsData))
+ {
+ m_logger->LogDebug("Attempting to find VirtualFunctionTables in segment %llx", segment->GetStart());
+ scan(segment);
+ }
+ else if (checkWritableRData && rdataSection && rdataSection->GetStart() == segment->GetStart())
+ {
+ m_logger->LogDebug("Attempting to find VirtualFunctionTables in writable rdata segment %llx",
+ segment->GetStart());
+ scan(segment);
+ }
+ }
+ }
+
+ auto GetCachedVFTInfo = [&](uint64_t vftAddr, const ClassInfo& classInfo) {
+ // Check in the cache so that we don't process vfts more than once.
+ auto cachedVftInfo = vftFinishedMap.find(vftAddr);
+ if (cachedVftInfo != vftFinishedMap.end())
+ return cachedVftInfo->second;
+ auto vftInfo = ProcessVFT(vftAddr, classInfo);
+ vftFinishedMap[vftAddr] = vftInfo;
+ return vftInfo;
+ };
+
+ for (const auto &[coLocatorAddr, vftAddr]: vftMap)
+ {
+ auto classInfo = m_classInfo.find(coLocatorAddr)->second;
+ if (classInfo.baseClassName.has_value())
+ {
+ // Process base vtable and add it to the class info.
+ for (auto& [baseCoLocAddr, baseClassInfo] : m_classInfo)
+ {
+ if (baseClassInfo.className == classInfo.baseClassName.value())
+ {
+ uint64_t baseVftAddr = vftMap[baseCoLocAddr];
+ if (auto baseVftInfo = GetCachedVFTInfo(baseVftAddr, baseClassInfo))
+ {
+ classInfo.baseVft = baseVftInfo.value();
+ break;
+ }
+ }
+ }
+ }
+
+ if (auto vftInfo = GetCachedVFTInfo(vftAddr, classInfo))
+ {
+ classInfo.vft = vftInfo.value();
+ }
+ }
+
+ auto end_time = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double> elapsed_time = end_time - start_time;
+ m_logger->LogInfo("ProcessVFT took %f seconds", elapsed_time.count());
+}
diff --git a/plugins/rtti/microsoft.h b/plugins/rtti/microsoft.h
new file mode 100644
index 00000000..b67431ec
--- /dev/null
+++ b/plugins/rtti/microsoft.h
@@ -0,0 +1,88 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+#include "rtti.h"
+
+namespace BinaryNinja::RTTI::Microsoft {
+ struct BaseClassArray
+ {
+ uint32_t length;
+ std::vector<uint64_t> descriptors;
+
+ BaseClassArray(BinaryView *view, uint64_t address, uint32_t length);
+ };
+
+ struct ClassHierarchyDescriptor
+ {
+ uint32_t signature;
+ uint32_t attributes;
+ uint32_t numBaseClasses;
+ int32_t pBaseClassArray;
+
+ ClassHierarchyDescriptor(BinaryView *view, uint64_t address);
+ };
+
+ struct BaseClassDescriptor
+ {
+ int32_t pTypeDescriptor;
+ uint32_t numContainedBases;
+ int32_t where_mdisp;
+ int32_t where_pdisp;
+ int32_t where_vdisp;
+ uint32_t attributes;
+ int32_t pClassHierarchyDescriptor;
+
+ BaseClassDescriptor(BinaryView *view, uint64_t address);
+ };
+
+ struct TypeDescriptor
+ {
+ uint64_t pVFTable;
+ uint64_t spare;
+ std::string name;
+
+ TypeDescriptor(BinaryView *view, uint64_t address);
+ };
+
+ struct CompleteObjectLocator
+ {
+ uint32_t signature;
+ uint32_t offset;
+ uint32_t cdOffset;
+ int32_t pTypeDescriptor;
+ int32_t pClassHierarchyDescriptor;
+ // Only on 64 bit
+ int32_t pSelf;
+
+ CompleteObjectLocator(BinaryView *view, uint64_t address);
+ };
+
+ class MicrosoftRTTIProcessor
+ {
+ Ref<BinaryView> m_view;
+ Ref<Logger> m_logger;
+ bool allowMangledClassNames;
+ bool allowAnonymousClassNames;
+ bool checkWritableRData;
+ bool virtualFunctionTableSweep;
+
+ std::map<uint64_t, ClassInfo> m_classInfo;
+
+ std::set<uint64_t> m_visitedClassHierarchyDescAddrs;
+
+ void DeserializedMetadata(const Ref<Metadata> &metadata);
+
+ std::optional<ClassInfo> ProcessRTTI(uint64_t coLocatorAddr);
+
+ std::optional<VirtualFunctionTableInfo> ProcessVFT(uint64_t vftAddr, const ClassInfo &classInfo);
+
+ public:
+ MicrosoftRTTIProcessor(const Ref<BinaryView> &view, bool useMangled = true, bool checkRData = true, bool vftSweep = true, bool allowAnonymous = true);
+
+ Ref<Metadata> SerializedMetadata();
+
+ void ProcessRTTI();
+
+ void ProcessVFT();
+ };
+} \ No newline at end of file
diff --git a/plugins/rtti/plugin.cpp b/plugins/rtti/plugin.cpp
new file mode 100644
index 00000000..4ec833be
--- /dev/null
+++ b/plugins/rtti/plugin.cpp
@@ -0,0 +1,106 @@
+#include "rtti.h"
+#include "microsoft.h"
+#include "itanium.h"
+
+#include <thread>
+
+using namespace BinaryNinja;
+
+// TODO: Split the activities so that there is two for microsoft and itanium.
+
+bool MetadataExists(const Ref<BinaryView>& view)
+{
+ return view->QueryMetadata(VIEW_METADATA_RTTI) != nullptr;
+}
+
+
+void RTTIAnalysis(const Ref<AnalysisContext>& analysisContext)
+{
+ auto view = analysisContext->GetBinaryView();
+ auto platform = view->GetDefaultPlatform();
+ if (!platform)
+ return;
+ auto platformName = platform->GetName();
+ if (platformName.find("window") != std::string::npos)
+ {
+ // We currently only want to check for MSVC rtti on windows platforms
+ auto processor = RTTI::Microsoft::MicrosoftRTTIProcessor(view);
+ processor.ProcessRTTI();
+ view->StoreMetadata(VIEW_METADATA_RTTI, processor.SerializedMetadata(), true);
+ }
+ else
+ {
+ // TODO: We currently only want to check for itanium rtti on non windows platforms
+ auto processor = RTTI::Itanium::ItaniumRTTIProcessor(view);
+ processor.ProcessRTTI();
+ // view->StoreMetadata(VIEW_METADATA_RTTI, processor.SerializedMetadata(), true);
+ }
+}
+
+
+void VFTAnalysis(const Ref<AnalysisContext>& analysisContext)
+{
+ auto view = analysisContext->GetBinaryView();
+ if (!MetadataExists(view))
+ return;
+ // TODO: Run for both itanium and ms (depending on platform)
+ auto processor = RTTI::Microsoft::MicrosoftRTTIProcessor(view);
+ processor.ProcessVFT();
+ view->StoreMetadata(VIEW_METADATA_RTTI, processor.SerializedMetadata(), true);
+}
+
+void MakeItaniumRTTIHere(Ref<BinaryView> view, uint64_t addr)
+{
+ auto processor = RTTI::Itanium::ItaniumRTTIProcessor(view);
+ processor.ProcessRTTI(addr);
+}
+
+
+
+extern "C" {
+ BN_DECLARE_CORE_ABI_VERSION
+
+ BINARYNINJAPLUGIN bool CorePluginInit()
+ {
+ // TODO: In the future we will have a function level workflow which:
+ // TODO: 1. Uses MSVC metadata to identify if a function is apart of a VFT
+ // TODO: a. Or possibly we can tag some info to the function as apart of the VFT analysis, this would save a lookup.
+ // TODO: 2. Identify if the function is unique to a class, renaming and retyping if true
+ // TODO: 3. Identify functions which address a VFT and are probably a constructor (alloc use), retyping if true
+ // TODO: 4. Identify functions which address a VFT and are probably a deconstructor (free use), retyping if true
+ Ref<Workflow> rttiMetaWorkflow = Workflow::Instance("core.module.metaAnalysis")->Clone("core.module.metaAnalysis");
+
+ PluginCommand::RegisterForAddress("Itanium\\Make RTTI Here", "", MakeItaniumRTTIHere);
+
+ // Add RTTI analysis.
+ rttiMetaWorkflow->RegisterActivity(R"~({
+ "title": "RTTI Analysis",
+ "name": "plugin.rtti.rttiAnalysis",
+ "role": "action",
+ "description": "This analysis step attempts to parse and symbolize rtti information.",
+ "eligibility": {
+ "runOnce": true,
+ "auto": {}
+ }
+ })~", &RTTIAnalysis);
+ // Add Virtual Function Table analysis.
+ rttiMetaWorkflow->RegisterActivity(R"~({
+ "title": "VFT Analysis",
+ "name": "plugin.rtti.vftAnalysis",
+ "role": "action",
+ "description": "This analysis step attempts to parse and symbolize virtual function table information.",
+ "eligibility": {
+ "runOnce": true,
+ "auto": {}
+ }
+ })~", &VFTAnalysis);
+
+ // Run rtti before debug info is applied.
+ rttiMetaWorkflow->Insert("core.module.loadDebugInfo", "plugin.rtti.rttiAnalysis");
+ // Run vft after functions have analyzed (so that the virtual functions have analyzed)
+ rttiMetaWorkflow->Insert("core.module.notifyCompletion", "plugin.rtti.vftAnalysis");
+ Workflow::RegisterWorkflow(rttiMetaWorkflow);
+
+ return true;
+ }
+} \ No newline at end of file
diff --git a/plugins/rtti/rtti.cpp b/plugins/rtti/rtti.cpp
new file mode 100644
index 00000000..d99983ee
--- /dev/null
+++ b/plugins/rtti/rtti.cpp
@@ -0,0 +1,112 @@
+#include "rtti.h"
+
+using namespace BinaryNinja;
+using namespace BinaryNinja::RTTI;
+
+
+std::optional<std::string> RTTI::DemangleNameMS(BinaryView* view, bool allowMangled, const std::string &mangledName)
+{
+ QualifiedName demangledName = {};
+ Ref<Type> outType = {};
+ if (!DemangleMS(view->GetDefaultArchitecture(), mangledName, outType, demangledName, true))
+ return DemangleNameLLVM(allowMangled, mangledName);
+ return demangledName.GetString();
+}
+
+
+std::optional<std::string> RTTI::DemangleNameGNU3(BinaryView* view, bool allowMangled, const std::string &mangledName)
+{
+ QualifiedName demangledName = {};
+ Ref<Type> outType = {};
+ if (!DemangleGNU3(view->GetDefaultArchitecture(), mangledName, outType, demangledName, true))
+ return DemangleNameLLVM(allowMangled, mangledName);
+ return demangledName.GetString();
+}
+
+
+std::optional<std::string> RTTI::DemangleNameLLVM(bool allowMangled, const std::string &mangledName)
+{
+ QualifiedName demangledName = {};
+ Ref<Type> outType = {};
+ if (!DemangleLLVM(mangledName, demangledName, true))
+ return allowMangled ? std::optional(mangledName) : std::nullopt;
+ auto demangledNameStr = demangledName.GetString();
+ size_t beginFind = demangledNameStr.find_first_of(' ');
+ if (beginFind != std::string::npos)
+ demangledNameStr.erase(0, beginFind + 1);
+ size_t endFind = demangledNameStr.find(" `RTTI Type Descriptor Name'");
+ if (endFind != std::string::npos)
+ demangledNameStr.erase(endFind, demangledNameStr.length());
+ return demangledNameStr;
+}
+
+
+Ref<Metadata> ClassInfo::SerializedMetadata()
+{
+ std::map<std::string, Ref<Metadata> > classInfoMeta;
+ classInfoMeta["className"] = new Metadata(className);
+ if (baseClassName.has_value())
+ classInfoMeta["baseClassName"] = new Metadata(baseClassName.value());
+ if (classOffset.has_value())
+ classInfoMeta["classOffset"] = new Metadata(classOffset.value());
+ if (vft.has_value())
+ classInfoMeta["vft"] = vft->SerializedMetadata();
+ // NOTE: We omit baseVft as it can be resolved manually and just bloats the size.
+ return new Metadata(classInfoMeta);
+}
+
+
+ClassInfo ClassInfo::DeserializedMetadata(const Ref<Metadata> &metadata)
+{
+ std::map<std::string, Ref<Metadata> > classInfoMeta = metadata->GetKeyValueStore();
+ ClassInfo info = {classInfoMeta["className"]->GetString()};
+ if (classInfoMeta.find("baseClassName") != classInfoMeta.end())
+ info.baseClassName = classInfoMeta["baseClassName"]->GetString();
+ if (classInfoMeta.find("classOffset") != classInfoMeta.end())
+ info.classOffset = classInfoMeta["classOffset"]->GetUnsignedInteger();
+ if (classInfoMeta.find("vft") != classInfoMeta.end())
+ info.vft = VirtualFunctionTableInfo::DeserializedMetadata(classInfoMeta["vft"]);
+ return info;
+}
+
+
+Ref<Metadata> VirtualFunctionTableInfo::SerializedMetadata()
+{
+ std::vector<Ref<Metadata> > funcsMeta;
+ funcsMeta.reserve(virtualFunctions.size());
+ for (auto &vFunc: virtualFunctions)
+ funcsMeta.emplace_back(vFunc.SerializedMetadata());
+ std::map<std::string, Ref<Metadata> > vftMeta;
+ vftMeta["address"] = new Metadata(address);
+ vftMeta["functions"] = new Metadata(funcsMeta);
+ return new Metadata(vftMeta);
+}
+
+
+VirtualFunctionTableInfo VirtualFunctionTableInfo::DeserializedMetadata(const Ref<Metadata> &metadata)
+{
+ std::map<std::string, Ref<Metadata> > vftMeta = metadata->GetKeyValueStore();
+ VirtualFunctionTableInfo vftInfo = {vftMeta["address"]->GetUnsignedInteger()};
+ if (vftMeta.find("functions") != vftMeta.end())
+ {
+ for (auto &entry: vftMeta["functions"]->GetArray())
+ vftInfo.virtualFunctions.emplace_back(VirtualFunctionInfo::DeserializedMetadata(entry));
+ }
+ return vftInfo;
+}
+
+
+Ref<Metadata> VirtualFunctionInfo::SerializedMetadata()
+{
+ std::map<std::string, Ref<Metadata> > vFuncMeta;
+ vFuncMeta["address"] = new Metadata(funcAddr);
+ return new Metadata(vFuncMeta);
+}
+
+
+VirtualFunctionInfo VirtualFunctionInfo::DeserializedMetadata(const Ref<Metadata> &metadata)
+{
+ std::map<std::string, Ref<Metadata> > vFuncMeta = metadata->GetKeyValueStore();
+ VirtualFunctionInfo vFuncInfo = {vFuncMeta["address"]->GetUnsignedInteger()};
+ return vFuncInfo;
+} \ No newline at end of file
diff --git a/plugins/rtti/rtti.h b/plugins/rtti/rtti.h
new file mode 100644
index 00000000..e53e1dac
--- /dev/null
+++ b/plugins/rtti/rtti.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+
+constexpr const char *VIEW_METADATA_RTTI = "rtti";
+constexpr int RTTI_CONFIDENCE = 100;
+
+namespace BinaryNinja::RTTI {
+ std::optional<std::string> DemangleNameMS(BinaryView* view, bool allowMangled, const std::string &mangledName);
+
+ std::optional<std::string> DemangleNameGNU3(BinaryView* view, bool allowMangled, const std::string &mangledName);
+
+ std::optional<std::string> DemangleNameLLVM(bool allowMangled, const std::string &mangledName);
+
+ struct VirtualFunctionInfo
+ {
+ uint64_t funcAddr;
+
+ Ref<Metadata> SerializedMetadata();
+
+ static VirtualFunctionInfo DeserializedMetadata(const Ref<Metadata> &metadata);
+ };
+
+ struct VirtualFunctionTableInfo
+ {
+ uint64_t address;
+ std::vector<VirtualFunctionInfo> virtualFunctions;
+
+ Ref<Metadata> SerializedMetadata();
+
+ static VirtualFunctionTableInfo DeserializedMetadata(const Ref<Metadata> &metadata);
+ };
+
+ // TODO: This needs to have some flags. Virtual, pure iirc.
+ struct ClassInfo
+ {
+ std::string className;
+ std::optional<std::string> baseClassName;
+ std::optional<uint64_t> classOffset;
+ std::optional<VirtualFunctionTableInfo> vft;
+ std::optional<VirtualFunctionTableInfo> baseVft;
+
+ Ref<Metadata> SerializedMetadata();
+
+ static ClassInfo DeserializedMetadata(const Ref<Metadata> &metadata);
+ };
+} \ No newline at end of file