Skip to content

Commit 93e9284

Browse files
committed
Consolidate metadata for RTTI processors and a bunch of misc fixes
Also allows for both processors to be ran for a single binary
1 parent 845dcf2 commit 93e9284

File tree

5 files changed

+148
-99
lines changed

5 files changed

+148
-99
lines changed

plugins/rtti/itanium.cpp

Lines changed: 50 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -238,35 +238,6 @@ 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-
270241
std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
271242
{
272243
// TODO: You cant get subobject offsets from rtti, its stored above this ptr in vtable.
@@ -279,7 +250,7 @@ std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
279250
auto className = DemangleNameItanium(m_view, allowMangledClassNames, typeInfo.type_name);
280251
if (!className.has_value())
281252
return std::nullopt;
282-
auto classInfo = ClassInfo{className.value()};
253+
auto classInfo = ClassInfo{RTTIProcessorType::Itanium, className.value()};
283254

284255
auto typeInfoName = fmt::format("_typeinfo_for_{}", classInfo.className);
285256
auto typeInfoSymbol = m_view->GetSymbolByAddress(objectAddr);
@@ -303,6 +274,8 @@ std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
303274
return std::nullopt;
304275
}
305276
classInfo.baseClassName = baseClassName;
277+
// NOTE: The base class offset is not able to be resolved here.
278+
// NOTE: To resolve the base class offset you must go to the vtable.
306279
m_view->DefineDataVariable(objectAddr, Confidence(SIClassTypeInfoType(m_view), 255));
307280
}
308281
else if (typeInfoVariant == TIVVMIClass)
@@ -321,12 +294,12 @@ std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
321294
}
322295

323296

324-
std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVTT(uint64_t vttAddr, const ClassInfo &classInfo)
297+
std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_t vftAddr, ClassInfo &classInfo)
325298
{
326-
VirtualFunctionTableInfo vttInfo = {vttAddr};
327-
// Gather all virtual functions
299+
VirtualFunctionTableInfo vftInfo = {vftAddr};
328300
BinaryReader reader = BinaryReader(m_view);
329-
reader.Seek(vttAddr);
301+
reader.Seek(vftAddr);
302+
// Gather all virtual functions
330303
std::vector<Ref<Function> > virtualFunctions = {};
331304
while (true)
332305
{
@@ -340,6 +313,7 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVTT(uint64_
340313
// Last CompleteObjectLocator or hit the next CompleteObjectLocator
341314
break;
342315
}
316+
// TODO: Sometimes vFunc idx will be zeroed.
343317
// TODO: Is likely a function check here?
344318
m_logger->LogDebug("Discovered function from virtual function table... %llx", vFuncAddr);
345319
auto vFunc = m_view->AddFunctionForAnalysis(m_view->GetDefaultPlatform(), vFuncAddr, true);
@@ -351,19 +325,33 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVTT(uint64_
351325

352326
if (virtualFunctions.empty())
353327
{
354-
m_logger->LogDebug("Skipping empty virtual function table... %llx", vttAddr);
328+
m_logger->LogDebug("Skipping empty virtual function table... %llx", vftAddr);
355329
return std::nullopt;
356330
}
357331

332+
// All vft verification has been done, we can write the classOffset now.
333+
if (classInfo.baseClassName.has_value() && !classInfo.classOffset.has_value())
334+
{
335+
// Because we have this we _need_ to have the adjustment stuff.
336+
// NOTE: We assume two 0x4 ints with the first being what we want.
337+
// NOTE: This is where we actually classOffset is pulled.
338+
reader.Seek(vftAddr - 0x10);
339+
auto adjustmentOffset = static_cast<int32_t>(reader.Read32());
340+
auto _what = static_cast<int32_t>(reader.Read32());
341+
uint64_t classOffset = std::abs(adjustmentOffset);
342+
classInfo.classOffset = classOffset;
343+
}
344+
345+
358346
for (auto &func: virtualFunctions)
359-
vttInfo.virtualFunctions.emplace_back(VirtualFunctionInfo{func->GetStart()});
347+
vftInfo.virtualFunctions.emplace_back(VirtualFunctionInfo{func->GetStart()});
360348

361349
// Create virtual function table type
362350
auto vftTypeName = fmt::format("{}::VTable", classInfo.className);
363351
if (classInfo.baseClassName.has_value())
364352
{
365-
vftTypeName = fmt::format("{}::{}", classInfo.baseClassName.value(), vftTypeName);
366353
// TODO: What is the correct form for the name?
354+
vftTypeName = fmt::format("{}::{}", classInfo.baseClassName.value(), vftTypeName);
367355
}
368356
// TODO: Hack the debug type id is used here to allow the PDB type (debug info) to overwrite the RTTI vtable type.
369357
auto typeId = Type::GenerateAutoDebugTypeId(vftTypeName);
@@ -376,16 +364,16 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVTT(uint64_
376364
vftBuilder.SetPropagateDataVariableReferences(true);
377365
size_t vFuncIdx = 0;
378366

379-
// Until https://github.com/Vector35/binaryninja-api/issues/5982 is fixed
367+
// TODO: Until https://github.com/Vector35/binaryninja-api/issues/5982 is fixed
380368
auto vftSize = virtualFunctions.size() * addrSize;
381369
vftBuilder.SetWidth(vftSize);
382370

383371
if (auto baseVft = classInfo.baseVft)
384372
{
385-
if (classInfo.baseVft->virtualFunctions.size() <= virtualFunctions.size())
373+
if (baseVft->virtualFunctions.size() <= virtualFunctions.size())
386374
{
387375
// Adjust the current vFunc index to the end of the shared vFuncs.
388-
vFuncIdx = classInfo.baseVft->virtualFunctions.size();
376+
vFuncIdx = baseVft->virtualFunctions.size();
389377
virtualFunctions.erase(virtualFunctions.begin(), virtualFunctions.begin() + vFuncIdx);
390378
// We should set the vtable as a base class so that xrefs are propagated (among other things).
391379
// NOTE: this means that `this` params will be assumed pre-adjusted, this is normally fine assuming type propagation
@@ -399,7 +387,7 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVTT(uint64_
399387
}
400388
else
401389
{
402-
LogWarn("Skipping adjustments for base VFT with more functions than sub VFT... %llx", vttAddr);
390+
LogWarn("Skipping adjustments for base VFT with more functions than sub VFT... %llx", vftAddr);
403391
}
404392
}
405393

@@ -425,20 +413,22 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVTT(uint64_
425413
Confidence(TypeBuilder::StructureType(vftBuilder.Finalize()).Finalize(), RTTI_CONFIDENCE));
426414
}
427415

428-
auto vftName = fmt::format("_vtable_for_", classInfo.className);
416+
auto vftName = fmt::format("_vtable_for_{}", classInfo.className);
417+
// TODO: How to display base classes?
429418
if (classInfo.baseClassName.has_value())
430419
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;
420+
auto vftSymbol = m_view->GetSymbolByAddress(vftAddr);
421+
if (vftSymbol != nullptr)
422+
m_view->UndefineAutoSymbol(vftSymbol);
423+
m_view->DefineAutoSymbol(new Symbol{DataSymbol, vftName, vftAddr});
424+
m_view->DefineDataVariable(vftAddr, Confidence(Type::NamedType(m_view, vftTypeName), RTTI_CONFIDENCE));
425+
return vftInfo;
437426
}
438427

439428

440-
ItaniumRTTIProcessor::ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool useMangled, bool checkRData, bool vftSweep) : m_view(view)
429+
ItaniumRTTIProcessor::ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool useMangled, bool checkRData, bool vftSweep)
441430
{
431+
m_view = view;
442432
m_logger = new Logger("Itanium RTTI");
443433
allowMangledClassNames = useMangled;
444434
checkWritableRData = checkRData;
@@ -449,7 +439,7 @@ ItaniumRTTIProcessor::ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool use
449439
if (metadata != nullptr)
450440
{
451441
// Load in metadata to the processor.
452-
DeserializedMetadata(metadata);
442+
DeserializedMetadata(RTTIProcessorType::Itanium, metadata);
453443
}
454444
}
455445

@@ -485,7 +475,7 @@ void ItaniumRTTIProcessor::ProcessRTTI()
485475
}
486476

487477

488-
void ItaniumRTTIProcessor::ProcessVTT()
478+
void ItaniumRTTIProcessor::ProcessVFT()
489479
{
490480
std::map<uint64_t, uint64_t> vftMap = {};
491481
std::map<uint64_t, std::optional<VirtualFunctionTableInfo>> vftFinishedMap = {};
@@ -494,6 +484,10 @@ void ItaniumRTTIProcessor::ProcessVTT()
494484
{
495485
for (auto &ref: m_view->GetDataReferences(coLocatorAddr))
496486
{
487+
// Skip refs from other type info.
488+
DataVariable dv;
489+
if (m_view->GetDataVariableAtAddress(ref, dv) && m_classInfo.find(dv.address) != m_classInfo.end())
490+
continue;
497491
// TODO: This is not pointing at where it should, remember that the vtable will be inside another structure.
498492
auto vftAddr = ref + m_view->GetAddressSize();
499493
vftMap[coLocatorAddr] = vftAddr;
@@ -537,12 +531,12 @@ void ItaniumRTTIProcessor::ProcessVTT()
537531
}
538532
}
539533

540-
auto GetCachedVFTInfo = [&](uint64_t vftAddr, const ClassInfo& classInfo) {
534+
auto GetCachedVFTInfo = [&](uint64_t vftAddr, ClassInfo& classInfo) {
541535
// Check in the cache so that we don't process vfts more than once.
542536
auto cachedVftInfo = vftFinishedMap.find(vftAddr);
543537
if (cachedVftInfo != vftFinishedMap.end())
544538
return cachedVftInfo->second;
545-
auto vftInfo = ProcessVTT(vftAddr, classInfo);
539+
auto vftInfo = ProcessVFT(vftAddr, classInfo);
546540
vftFinishedMap[vftAddr] = vftInfo;
547541
return vftInfo;
548542
};
@@ -553,7 +547,7 @@ void ItaniumRTTIProcessor::ProcessVTT()
553547
if (classInfo.baseClassName.has_value())
554548
{
555549
// Process base vtable and add it to the class info.
556-
for (auto& [baseCoLocAddr, baseClassInfo] : m_classInfo)
550+
for (auto [baseCoLocAddr, baseClassInfo] : m_classInfo)
557551
{
558552
if (baseClassInfo.className == classInfo.baseClassName.value())
559553
{
@@ -569,6 +563,8 @@ void ItaniumRTTIProcessor::ProcessVTT()
569563

570564
if (auto vftInfo = GetCachedVFTInfo(vftAddr, classInfo))
571565
classInfo.vft = vftInfo.value();
566+
567+
m_classInfo[coLocatorAddr] = classInfo;
572568
}
573569

574570
auto end_time = std::chrono::high_resolution_clock::now();

plugins/rtti/itanium.h

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -108,29 +108,20 @@ namespace BinaryNinja::RTTI::Itanium {
108108
PointerToMemberTypeInfo(BinaryView *view, uint64_t address);
109109
};
110110

111-
class ItaniumRTTIProcessor
111+
class ItaniumRTTIProcessor : public RTTIProcessor
112112
{
113-
Ref<BinaryView> m_view;
114-
Ref<Logger> m_logger;
115113
bool allowMangledClassNames;
116114
bool checkWritableRData;
117115
bool virtualFunctionTableSweep;
118116

119-
std::map<uint64_t, ClassInfo> m_classInfo;
120-
121-
void DeserializedMetadata(const Ref<Metadata> &metadata);
122-
123-
std::optional<VirtualFunctionTableInfo> ProcessVTT(uint64_t vttAddr, const ClassInfo &classInfo);
117+
std::optional<ClassInfo> ProcessRTTI(uint64_t objectAddr) override;
124118

119+
std::optional<VirtualFunctionTableInfo> ProcessVFT(uint64_t vftAddr, ClassInfo &classInfo) override;
125120
public:
126-
ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool useMangled = true, bool checkRData = true, bool vttSweep = true);
127-
128-
Ref<Metadata> SerializedMetadata();
129-
130-
void ProcessRTTI();
121+
explicit ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool useMangled = true, bool checkRData = true, bool vttSweep = true);
131122

132-
std::optional<ClassInfo> ProcessRTTI(uint64_t objectAddr);
123+
void ProcessRTTI() override;
133124

134-
void ProcessVTT();
125+
void ProcessVFT() override;
135126
};
136127
}

plugins/rtti/plugin.cpp

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22
#include "microsoft.h"
33
#include "itanium.h"
44

5-
#include <thread>
6-
75
using namespace BinaryNinja;
86

9-
// TODO: Split the activities so that there is two for microsoft and itanium.
107

118
bool MetadataExists(const Ref<BinaryView>& view)
129
{
@@ -28,14 +25,10 @@ void RTTIAnalysis(const Ref<AnalysisContext>& analysisContext)
2825
processor.ProcessRTTI();
2926
view->StoreMetadata(VIEW_METADATA_RTTI, processor.SerializedMetadata(), true);
3027
}
31-
else
32-
{
33-
// TODO: We currently only want to check for itanium rtti on non windows platforms
34-
// TODO: This needs to always run.
35-
auto processor = RTTI::Itanium::ItaniumRTTIProcessor(view);
36-
processor.ProcessRTTI();
37-
view->StoreMetadata(VIEW_METADATA_RTTI, processor.SerializedMetadata(), true);
38-
}
28+
29+
auto processor = RTTI::Itanium::ItaniumRTTIProcessor(view);
30+
processor.ProcessRTTI();
31+
view->StoreMetadata(VIEW_METADATA_RTTI, processor.SerializedMetadata(), true);
3932
}
4033

4134

@@ -44,19 +37,13 @@ void VFTAnalysis(const Ref<AnalysisContext>& analysisContext)
4437
auto view = analysisContext->GetBinaryView();
4538
if (!MetadataExists(view))
4639
return;
47-
// TODO: Run for both itanium and ms (depending on platform)
48-
auto processor = RTTI::Microsoft::MicrosoftRTTIProcessor(view);
49-
processor.ProcessVFT();
40+
auto microsoftProcessor = RTTI::Microsoft::MicrosoftRTTIProcessor(view);
41+
microsoftProcessor.ProcessVFT();
42+
// TODO: We have to store the data for the second processor to pick up the info.
43+
view->StoreMetadata(VIEW_METADATA_RTTI, microsoftProcessor.SerializedMetadata(), true);
5044
auto itaniumProcessor = RTTI::Itanium::ItaniumRTTIProcessor(view);
51-
itaniumProcessor.ProcessVTT();
52-
view->StoreMetadata(VIEW_METADATA_RTTI, processor.SerializedMetadata(), true);
53-
}
54-
55-
56-
void MakeItaniumRTTIHere(Ref<BinaryView> view, uint64_t addr)
57-
{
58-
auto processor = RTTI::Itanium::ItaniumRTTIProcessor(view);
59-
processor.ProcessRTTI(addr);
45+
itaniumProcessor.ProcessVFT();
46+
view->StoreMetadata(VIEW_METADATA_RTTI, itaniumProcessor.SerializedMetadata(), true);
6047
}
6148

6249

@@ -73,8 +60,6 @@ extern "C" {
7360
// TODO: 4. Identify functions which address a VFT and are probably a deconstructor (free use), retyping if true
7461
Ref<Workflow> rttiMetaWorkflow = Workflow::Instance("core.module.metaAnalysis")->Clone("core.module.metaAnalysis");
7562

76-
PluginCommand::RegisterForAddress("Itanium\\Make RTTI Here", "", MakeItaniumRTTIHere);
77-
7863
// Add RTTI analysis.
7964
rttiMetaWorkflow->RegisterActivity(R"~({
8065
"title": "RTTI Analysis",

0 commit comments

Comments
 (0)