@@ -238,6 +238,35 @@ std::optional<TypeInfoVariant> ReadTypeInfoVariant(BinaryView *view, uint64_t ob
238238}
239239
240240
241+ Ref<Metadata> ItaniumRTTIProcessor::SerializedMetadata ()
242+ {
243+ std::map<std::string, Ref<Metadata> > classesMeta;
244+ for (auto &[coLocatorAddr, classInfo]: m_classInfo)
245+ {
246+ auto addrStr = std::to_string (coLocatorAddr);
247+ classesMeta[addrStr] = classInfo.SerializedMetadata ();
248+ }
249+
250+ std::map<std::string, Ref<Metadata> > msvcMeta;
251+ msvcMeta[" classes" ] = new Metadata (classesMeta);
252+ return new Metadata (msvcMeta);
253+ }
254+
255+
256+ void ItaniumRTTIProcessor::DeserializedMetadata (const Ref<Metadata> &metadata)
257+ {
258+ std::map<std::string, Ref<Metadata> > msvcMeta = metadata->GetKeyValueStore ();
259+ if (msvcMeta.find (" classes" ) != msvcMeta.end ())
260+ {
261+ for (auto &[objectAddrStr, classInfoMeta]: msvcMeta[" classes" ]->GetKeyValueStore ())
262+ {
263+ uint64_t objectAddr = std::stoull (objectAddrStr);
264+ m_classInfo[objectAddr] = ClassInfo::DeserializedMetadata (classInfoMeta);
265+ }
266+ }
267+ }
268+
269+
241270std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI (uint64_t objectAddr)
242271{
243272 // TODO: You cant get subobject offsets from rtti, its stored above this ptr in vtable.
@@ -292,6 +321,122 @@ std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
292321}
293322
294323
324+ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVTT (uint64_t vttAddr, const ClassInfo &classInfo)
325+ {
326+ VirtualFunctionTableInfo vttInfo = {vttAddr};
327+ // Gather all virtual functions
328+ BinaryReader reader = BinaryReader (m_view);
329+ reader.Seek (vttAddr);
330+ std::vector<Ref<Function> > virtualFunctions = {};
331+ while (true )
332+ {
333+ uint64_t vFuncAddr = reader.ReadPointer ();
334+ auto funcs = m_view->GetAnalysisFunctionsForAddress (vFuncAddr);
335+ if (funcs.empty ())
336+ {
337+ Ref<Segment> segment = m_view->GetSegmentAt (vFuncAddr);
338+ if (segment == nullptr || !(segment->GetFlags () & (SegmentExecutable | SegmentDenyWrite)))
339+ {
340+ // Last CompleteObjectLocator or hit the next CompleteObjectLocator
341+ break ;
342+ }
343+ // TODO: Is likely a function check here?
344+ m_logger->LogDebug (" Discovered function from virtual function table... %llx" , vFuncAddr);
345+ auto vFunc = m_view->AddFunctionForAnalysis (m_view->GetDefaultPlatform (), vFuncAddr, true );
346+ funcs.emplace_back (vFunc);
347+ }
348+ // Only ever add one function.
349+ virtualFunctions.emplace_back (funcs.front ());
350+ }
351+
352+ if (virtualFunctions.empty ())
353+ {
354+ m_logger->LogDebug (" Skipping empty virtual function table... %llx" , vttAddr);
355+ return std::nullopt ;
356+ }
357+
358+ for (auto &func: virtualFunctions)
359+ vttInfo.virtualFunctions .emplace_back (VirtualFunctionInfo{func->GetStart ()});
360+
361+ // Create virtual function table type
362+ auto vftTypeName = fmt::format (" {}::VTable" , classInfo.className );
363+ if (classInfo.baseClassName .has_value ())
364+ {
365+ vftTypeName = fmt::format (" {}::{}" , classInfo.baseClassName .value (), vftTypeName);
366+ // TODO: What is the correct form for the name?
367+ }
368+ // TODO: Hack the debug type id is used here to allow the PDB type (debug info) to overwrite the RTTI vtable type.
369+ auto typeId = Type::GenerateAutoDebugTypeId (vftTypeName);
370+ Ref<Type> vftType = m_view->GetTypeById (typeId);
371+
372+ if (vftType == nullptr )
373+ {
374+ size_t addrSize = m_view->GetAddressSize ();
375+ StructureBuilder vftBuilder = {};
376+ vftBuilder.SetPropagateDataVariableReferences (true );
377+ size_t vFuncIdx = 0 ;
378+
379+ // Until https://github.com/Vector35/binaryninja-api/issues/5982 is fixed
380+ auto vftSize = virtualFunctions.size () * addrSize;
381+ vftBuilder.SetWidth (vftSize);
382+
383+ if (auto baseVft = classInfo.baseVft )
384+ {
385+ if (classInfo.baseVft ->virtualFunctions .size () <= virtualFunctions.size ())
386+ {
387+ // Adjust the current vFunc index to the end of the shared vFuncs.
388+ vFuncIdx = classInfo.baseVft ->virtualFunctions .size ();
389+ virtualFunctions.erase (virtualFunctions.begin (), virtualFunctions.begin () + vFuncIdx);
390+ // We should set the vtable as a base class so that xrefs are propagated (among other things).
391+ // NOTE: this means that `this` params will be assumed pre-adjusted, this is normally fine assuming type propagation
392+ // NOTE: never occurs on the vft types. Other-wise we need to change this.
393+ auto baseVftTypeName = fmt::format (" {}::VTable" , classInfo.baseClassName .value ());
394+ NamedTypeReferenceBuilder baseVftNTR;
395+ baseVftNTR.SetName (baseVftTypeName);
396+ // Width is unresolved here so that we can keep non-base vfuncs un-inherited.
397+ auto baseVftSize = vFuncIdx * addrSize;
398+ vftBuilder.SetBaseStructures ({ BaseStructure (baseVftNTR.Finalize (), 0 , baseVftSize) });
399+ }
400+ else
401+ {
402+ LogWarn (" Skipping adjustments for base VFT with more functions than sub VFT... %llx" , vttAddr);
403+ }
404+ }
405+
406+ for (auto &&vFunc: virtualFunctions)
407+ {
408+ auto vFuncName = fmt::format (" vFunc_{}" , vFuncIdx);
409+ // If we have a better name, use it.
410+ auto vFuncSymName = vFunc->GetSymbol ()->GetShortName ();
411+ if (vFuncSymName.compare (0 , 4 , " sub_" ) != 0 )
412+ vFuncName = vFunc->GetSymbol ()->GetShortName ();
413+ // MyClass::func -> func
414+ std::size_t pos = vFuncName.rfind (" ::" );
415+ if (pos != std::string::npos)
416+ vFuncName = vFuncName.substr (pos + 2 );
417+
418+ // NOTE: The analyzed function type might not be available here.
419+ auto vFuncOffset = vFuncIdx * addrSize;
420+ vftBuilder.AddMemberAtOffset (
421+ Type::PointerType (addrSize, vFunc->GetType (), true ), vFuncName, vFuncOffset);
422+ vFuncIdx++;
423+ }
424+ m_view->DefineType (typeId, vftTypeName,
425+ Confidence (TypeBuilder::StructureType (vftBuilder.Finalize ()).Finalize (), RTTI_CONFIDENCE));
426+ }
427+
428+ auto vftName = fmt::format (" _vtable_for_" , classInfo.className );
429+ if (classInfo.baseClassName .has_value ())
430+ vftName += fmt::format (" {{for `{}'}}" , classInfo.baseClassName .value ());
431+ auto vttSymbol = m_view->GetSymbolByAddress (vttAddr);
432+ if (vttSymbol != nullptr )
433+ m_view->UndefineAutoSymbol (vttSymbol);
434+ m_view->DefineAutoSymbol (new Symbol{DataSymbol, vftName, vttAddr});
435+ m_view->DefineDataVariable (vttAddr, Confidence (Type::NamedType (m_view, vftTypeName), RTTI_CONFIDENCE));
436+ return vttInfo;
437+ }
438+
439+
295440ItaniumRTTIProcessor::ItaniumRTTIProcessor (const Ref<BinaryView> &view, bool useMangled, bool checkRData, bool vftSweep) : m_view(view)
296441{
297442 m_logger = new Logger (" Itanium RTTI" );
@@ -303,9 +448,8 @@ ItaniumRTTIProcessor::ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool use
303448 auto metadata = view->QueryMetadata (VIEW_METADATA_RTTI);
304449 if (metadata != nullptr )
305450 {
306- // TODO: This will pull in microsoft RTTI, which is really weird behavior possibly.
307451 // Load in metadata to the processor.
308- // DeserializedMetadata(metadata);
452+ DeserializedMetadata (metadata);
309453 }
310454}
311455
@@ -338,4 +482,96 @@ void ItaniumRTTIProcessor::ProcessRTTI()
338482 auto end_time = std::chrono::high_resolution_clock::now ();
339483 std::chrono::duration<double > elapsed_time = end_time - start_time;
340484 m_logger->LogInfo (" ProcessRTTI took %f seconds" , elapsed_time.count ());
485+ }
486+
487+
488+ void ItaniumRTTIProcessor::ProcessVTT ()
489+ {
490+ std::map<uint64_t , uint64_t > vftMap = {};
491+ std::map<uint64_t , std::optional<VirtualFunctionTableInfo>> vftFinishedMap = {};
492+ auto start_time = std::chrono::high_resolution_clock::now ();
493+ for (auto &[coLocatorAddr, classInfo]: m_classInfo)
494+ {
495+ for (auto &ref: m_view->GetDataReferences (coLocatorAddr))
496+ {
497+ // TODO: This is not pointing at where it should, remember that the vtable will be inside another structure.
498+ auto vftAddr = ref + m_view->GetAddressSize ();
499+ vftMap[coLocatorAddr] = vftAddr;
500+ }
501+ }
502+
503+ if (virtualFunctionTableSweep)
504+ {
505+ BinaryReader optReader = BinaryReader (m_view);
506+ auto addrSize = m_view->GetAddressSize ();
507+ auto scan = [&](const Ref<Segment> &segment) {
508+ uint64_t startAddr = segment->GetStart ();
509+ uint64_t endAddr = segment->GetEnd ();
510+ for (uint64_t vtableAddr = startAddr; vtableAddr < endAddr - 0x10 ; vtableAddr += addrSize)
511+ {
512+ optReader.Seek (vtableAddr);
513+ uint64_t coLocatorAddr = optReader.ReadPointer ();
514+ auto coLocator = m_classInfo.find (coLocatorAddr);
515+ if (coLocator == m_classInfo.end ())
516+ continue ;
517+ // Found a vtable reference to colocator.
518+ vftMap[coLocatorAddr] = vtableAddr + addrSize;
519+ }
520+ };
521+
522+ // Scan data sections for virtual function tables.
523+ auto rdataSection = m_view->GetSectionByName (" .rdata" );
524+ for (const Ref<Segment> &segment: m_view->GetSegments ())
525+ {
526+ if (segment->GetFlags () == (SegmentReadable | SegmentContainsData))
527+ {
528+ m_logger->LogDebug (" Attempting to find VirtualFunctionTables in segment %llx" , segment->GetStart ());
529+ scan (segment);
530+ }
531+ else if (checkWritableRData && rdataSection && rdataSection->GetStart () == segment->GetStart ())
532+ {
533+ m_logger->LogDebug (" Attempting to find VirtualFunctionTables in writable rdata segment %llx" ,
534+ segment->GetStart ());
535+ scan (segment);
536+ }
537+ }
538+ }
539+
540+ auto GetCachedVFTInfo = [&](uint64_t vftAddr, const ClassInfo& classInfo) {
541+ // Check in the cache so that we don't process vfts more than once.
542+ auto cachedVftInfo = vftFinishedMap.find (vftAddr);
543+ if (cachedVftInfo != vftFinishedMap.end ())
544+ return cachedVftInfo->second ;
545+ auto vftInfo = ProcessVTT (vftAddr, classInfo);
546+ vftFinishedMap[vftAddr] = vftInfo;
547+ return vftInfo;
548+ };
549+
550+ for (const auto &[coLocatorAddr, vftAddr]: vftMap)
551+ {
552+ auto classInfo = m_classInfo.find (coLocatorAddr)->second ;
553+ if (classInfo.baseClassName .has_value ())
554+ {
555+ // Process base vtable and add it to the class info.
556+ for (auto & [baseCoLocAddr, baseClassInfo] : m_classInfo)
557+ {
558+ if (baseClassInfo.className == classInfo.baseClassName .value ())
559+ {
560+ uint64_t baseVftAddr = vftMap[baseCoLocAddr];
561+ if (auto baseVftInfo = GetCachedVFTInfo (baseVftAddr, baseClassInfo))
562+ {
563+ classInfo.baseVft = baseVftInfo.value ();
564+ break ;
565+ }
566+ }
567+ }
568+ }
569+
570+ if (auto vftInfo = GetCachedVFTInfo (vftAddr, classInfo))
571+ classInfo.vft = vftInfo.value ();
572+ }
573+
574+ auto end_time = std::chrono::high_resolution_clock::now ();
575+ std::chrono::duration<double > elapsed_time = end_time - start_time;
576+ m_logger->LogInfo (" ProcessVFT took %f seconds" , elapsed_time.count ());
341577}
0 commit comments