diff options
| author | Mason Reed <mason@vector35.com> | 2024-11-22 18:16:32 -0500 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-03-19 21:17:34 -0400 |
| commit | 9ff2b8d804a34941a6085af85b6749c20549240e (patch) | |
| tree | 9b78b478476cfb66fc68828087ad9ff99cdfacb5 /plugins/rtti | |
| parent | 7d66d87a4600c8954c585c3c690546fc56903d44 (diff) | |
Itanium RTTI scaffolding
Diffstat (limited to 'plugins/rtti')
| -rw-r--r-- | plugins/rtti/CMakeLists.txt | 46 | ||||
| -rw-r--r-- | plugins/rtti/README.md | 86 | ||||
| -rw-r--r-- | plugins/rtti/itanium.cpp | 356 | ||||
| -rw-r--r-- | plugins/rtti/itanium.h | 136 | ||||
| -rw-r--r-- | plugins/rtti/microsoft.cpp | 875 | ||||
| -rw-r--r-- | plugins/rtti/microsoft.h | 88 | ||||
| -rw-r--r-- | plugins/rtti/plugin.cpp | 106 | ||||
| -rw-r--r-- | plugins/rtti/rtti.cpp | 112 | ||||
| -rw-r--r-- | plugins/rtti/rtti.h | 47 |
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> §ion) { + 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> §ion: 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 |
