From e299a6c1bf74f0ca6d2c3d79135e8c23e6b9fbba Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 12 Jan 2026 10:58:59 -0800 Subject: [RTTI] Improve virtual function discovery - Allow extern functions to show up in a MSVC vtable - Fix https://github.com/Vector35/binaryninja-api/issues/7871 - Share code between itanium and msvc vft analysis, it is the same logic basically --- plugins/rtti/itanium.cpp | 58 +++++------------------------------------------- 1 file changed, 5 insertions(+), 53 deletions(-) (limited to 'plugins/rtti/itanium.cpp') diff --git a/plugins/rtti/itanium.cpp b/plugins/rtti/itanium.cpp index 6f4c7158..145969c0 100644 --- a/plugins/rtti/itanium.cpp +++ b/plugins/rtti/itanium.cpp @@ -14,22 +14,6 @@ constexpr const char *TYPE_SOURCE_ITANIUM = "rtti_itanium"; constexpr int MAX_FAILED_SCAN_ATTEMPTS = 10; -Ref GetRealSymbol(BinaryView *view, uint64_t relocAddr, uint64_t symAddr) -{ - if (view->IsOffsetExternSemantics(symAddr)) - { - // Because bases in the extern section are not 8 byte width only they will - // overlap with other externs, until https://github.com/Vector35/binaryninja-api/issues/6387 is fixed. - // Check relocation at objectAddr for symbol - for (const auto& r : view->GetRelocationsAt(relocAddr)) - if (auto relocSym = r->GetSymbol()) - return relocSym; - } - - return view->GetSymbolByAddress(symAddr); -} - - // Some fields are not always u32, use this if it goes from u32 -> u16 on 32bit uint64_t ArchFieldSize(BinaryView *view) { @@ -37,7 +21,6 @@ uint64_t ArchFieldSize(BinaryView *view) } - uint64_t TypeInfoSize(BinaryView *view) { return view->GetAddressSize() * 2; @@ -558,47 +541,16 @@ std::optional ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr) std::optional ItaniumRTTIProcessor::ProcessVFT(uint64_t vftAddr, ClassInfo &classInfo, std::optional baseClassInfo) { VirtualFunctionTableInfo vftInfo = {vftAddr}; - BinaryReader reader = BinaryReader(m_view); - reader.Seek(vftAddr); // Gather all virtual functions std::vector virtualFunctions = {}; + uint64_t currentVftEntry = vftAddr; while (true) { - uint64_t readOffset = reader.GetOffset(); - if (!m_view->IsValidOffset(readOffset)) + uint64_t vFuncAddr = 0; + const FunctionDiscoverState state = DiscoverVirtualFunction(currentVftEntry, vFuncAddr); + if (state == FunctionDiscoverState::Failed) break; - uint64_t vFuncAddr = reader.ReadPointer(); - auto funcs = m_view->GetAnalysisFunctionsForAddress(vFuncAddr); - if (funcs.empty()) - { - Ref segment = m_view->GetSegmentAt(vFuncAddr); - if (segment == nullptr || !(segment->GetFlags() & (SegmentExecutable | SegmentDenyWrite))) - { - // TODO: Sometimes vFunc idx will be zeroed iirc. - // We allow vfuncs to point to extern functions. - // TODO: Until https://github.com/Vector35/binaryninja-api/issues/5982 is fixed we need to check extern sym relocs instead of the symbol directly - auto vFuncSym = GetRealSymbol(m_view, reader.GetOffset(), vFuncAddr); - if (!vFuncSym) - break; - DataVariable dv; - bool foundDv = m_view->GetDataVariableAtAddress(vFuncAddr, dv); - // Last virtual function, or hit the next vtable. - if (!foundDv || !dv.type->m_object) - break; - // Void externs are very likely to be a func. - // TODO: Add some sanity checks for this! - if (!dv.type->IsFunction() && !(dv.type->IsVoid() && vFuncSym->GetType() == ExternalSymbol)) - break; - } - else - { - // TODO: Is likely a function check here? - m_logger->LogDebugF("Discovered function from virtual function table... {:#x}", vFuncAddr); - auto vftPlatform = m_view->GetDefaultPlatform()->GetAssociatedPlatformByAddress(vFuncAddr); - m_view->AddFunctionForAnalysis(vftPlatform, vFuncAddr, true); - } - } - // Only ever add one function. + currentVftEntry += m_view->GetAddressSize(); virtualFunctions.emplace_back(VirtualFunctionInfo{vFuncAddr}); } -- cgit v1.3.1