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 | |
| parent | 7d66d87a4600c8954c585c3c690546fc56903d44 (diff) | |
Itanium RTTI scaffolding
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/rtti/CMakeLists.txt (renamed from plugins/msvc_rtti/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | plugins/rtti/README.md (renamed from plugins/msvc_rtti/README.md) | 4 | ||||
| -rw-r--r-- | plugins/rtti/itanium.cpp | 356 | ||||
| -rw-r--r-- | plugins/rtti/itanium.h | 136 | ||||
| -rw-r--r-- | plugins/rtti/microsoft.cpp (renamed from plugins/msvc_rtti/rtti.cpp) | 0 | ||||
| -rw-r--r-- | plugins/rtti/microsoft.h (renamed from plugins/msvc_rtti/rtti.h) | 39 | ||||
| -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, 761 insertions, 39 deletions
diff --git a/plugins/msvc_rtti/CMakeLists.txt b/plugins/rtti/CMakeLists.txt index 085f13a3..085f13a3 100644 --- a/plugins/msvc_rtti/CMakeLists.txt +++ b/plugins/rtti/CMakeLists.txt diff --git a/plugins/msvc_rtti/README.md b/plugins/rtti/README.md index 1a6696d7..ef95222d 100644 --- a/plugins/msvc_rtti/README.md +++ b/plugins/rtti/README.md @@ -18,7 +18,7 @@ struct _RTTICompleteObjectLocator MapTrackView::`RTTI Complete Object Locator'{f } ``` -_The above listing includes type information deduced seperately through demangled names_ +_The above listing includes type information deduced separately through demangled names_ ## Example Virtual Function Table Listing @@ -38,7 +38,7 @@ struct QPaintDevice::MapTrackView::VTable MapTrackView::`vftable'{for `QPaintDev } ``` -_The above listing includes type information deduced seperately through demangled names_ +_The above listing includes type information deduced separately through demangled names_ ## Exposed Metadata 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/msvc_rtti/rtti.cpp b/plugins/rtti/microsoft.cpp index 07a33d61..07a33d61 100644 --- a/plugins/msvc_rtti/rtti.cpp +++ b/plugins/rtti/microsoft.cpp diff --git a/plugins/msvc_rtti/rtti.h b/plugins/rtti/microsoft.h index 0bb3a733..b67431ec 100644 --- a/plugins/msvc_rtti/rtti.h +++ b/plugins/rtti/microsoft.h @@ -1,10 +1,9 @@ #pragma once #include "binaryninjaapi.h" +#include "rtti.h" -constexpr const char *VIEW_METADATA_MSVC = "msvc"; - -namespace BinaryNinja { +namespace BinaryNinja::RTTI::Microsoft { struct BaseClassArray { uint32_t length; @@ -58,38 +57,6 @@ namespace BinaryNinja { CompleteObjectLocator(BinaryView *view, uint64_t address); }; - 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); - }; - - 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); - }; - class MicrosoftRTTIProcessor { Ref<BinaryView> m_view; @@ -105,8 +72,6 @@ namespace BinaryNinja { void DeserializedMetadata(const Ref<Metadata> &metadata); - std::optional<std::string> DemangleName(const std::string &mangledName); - std::optional<ClassInfo> ProcessRTTI(uint64_t coLocatorAddr); std::optional<VirtualFunctionTableInfo> ProcessVFT(uint64_t vftAddr, const ClassInfo &classInfo); 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 |
