Skip to content

Commit c11feee

Browse files
committed
Fix misc crashes with Itanium RTTI and improve 32bit support
32bit support still needs some fixes for the VMI offsets and other things, but its better than it was before. Likely need to do a little refactoring after this release to make the code less horrendous, but its fine for now.
1 parent 6e2520f commit c11feee

File tree

2 files changed

+104
-107
lines changed

2 files changed

+104
-107
lines changed

plugins/rtti/itanium.cpp

Lines changed: 100 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,49 @@ using namespace BinaryNinja::RTTI::Itanium;
1111
constexpr const char *TYPE_SOURCE_ITANIUM = "rtti_itanium";
1212

1313

14+
Ref<Symbol> GetRealSymbol(BinaryView *view, uint64_t relocAddr, uint64_t symAddr)
15+
{
16+
if (view->IsOffsetExternSemantics(symAddr))
17+
{
18+
// Because bases in the extern section are not 8 byte width only they will
19+
// overlap with other externs, until https://github.com/Vector35/binaryninja-api/issues/6387 is fixed.
20+
// Check relocation at objectAddr for symbol
21+
for (const auto& r : view->GetRelocationsAt(relocAddr))
22+
if (auto relocSym = r->GetSymbol())
23+
return relocSym;
24+
}
25+
26+
return view->GetSymbolByAddress(symAddr);
27+
}
28+
29+
30+
// Some fields are not always u32, use this if it goes from u32 -> u16 on 32bit
31+
uint64_t ArchFieldSize(BinaryView *view)
32+
{
33+
return view->GetAddressSize() / 2;
34+
}
35+
36+
37+
38+
uint64_t TypeInfoSize(BinaryView *view)
39+
{
40+
return view->GetAddressSize() * 2;
41+
}
42+
43+
1444
std::optional<TypeInfo> GetTypeInfo(BinaryView* view, uint64_t address)
1545
{
1646
// TODO: We really need a valid offset range thing.
17-
const auto typeInfoSize = view->GetAddressSize() * 2;
47+
const auto typeInfoSize = TypeInfoSize(view);
1848
if (!view->IsValidOffset(address) || !view->IsValidOffset(address + typeInfoSize))
1949
return std::nullopt;
2050
BinaryReader reader = BinaryReader(view);
2151
reader.Seek(address);
2252
auto base = reader.ReadPointer();
23-
if (!view->IsValidOffset(base))
53+
if (!view->IsValidOffset(base) || view->IsOffsetCodeSemantics(base))
2454
return std::nullopt;
2555
auto typeNameAddr = reader.ReadPointer();
26-
if (!view->IsValidOffset(typeNameAddr))
56+
if (!view->IsValidOffset(typeNameAddr) || view->IsOffsetCodeSemantics(typeNameAddr))
2757
return std::nullopt;
2858
reader.Seek(typeNameAddr);
2959
auto type_name = reader.ReadCString(512);
@@ -46,7 +76,7 @@ SIClassTypeInfo::SIClassTypeInfo(BinaryView *view, uint64_t address) : ClassType
4676
{
4777
BinaryReader reader = BinaryReader(view);
4878
// TODO: Manually seeking to the offset is ugly.
49-
reader.Seek(address + 0x10);
79+
reader.Seek(address + TypeInfoSize(view));
5080
base_type = reader.ReadPointer();
5181
}
5282

@@ -56,24 +86,38 @@ BaseClassTypeInfo::BaseClassTypeInfo(BinaryView *view, uint64_t address)
5686
BinaryReader reader = BinaryReader(view);
5787
reader.Seek(address);
5888
base_type = reader.ReadPointer();
59-
offset_flags = reader.Read32();
60-
offset_flags_masks = reader.Read32();
89+
// I thought the spec was pretty clear about these being u32
90+
// but apparently not? Or atleast on the gcc compiler I have these get turned to u16, disappointing.
91+
if (view->GetAddressSize() == 8)
92+
{
93+
offset_flags = reader.Read32();
94+
offset_flags_masks = reader.Read32();
95+
}
96+
else
97+
{
98+
offset_flags = reader.Read16();
99+
offset_flags_masks = reader.Read16();
100+
}
61101
}
62102

103+
uint64_t BaseClassTypeSize(BinaryView *view)
104+
{
105+
return view->GetAddressSize() + 0x8;
106+
}
63107

64108
VMIClassTypeInfo::VMIClassTypeInfo(BinaryView *view, uint64_t address) : ClassTypeInfo(view, address)
65109
{
66110
BinaryReader reader = BinaryReader(view);
67111
// TODO: Manually seeking to the offset is ugly.
68-
reader.Seek(address + 0x10);
112+
reader.Seek(address + TypeInfoSize(view));
69113
flags = reader.Read32();
70114
base_count = reader.Read32();
71115
base_info = {};
72116
for (size_t i = 0; i < base_count; i++)
73117
{
74118
uint64_t currentBaseAddr = reader.GetOffset();
75119
base_info.emplace_back(view, currentBaseAddr);
76-
reader.Seek(currentBaseAddr + 0x10);
120+
reader.Seek(currentBaseAddr + BaseClassTypeSize(view));
77121
}
78122
}
79123

@@ -115,7 +159,7 @@ Ref<Type> ClassTypeInfoType(BinaryView *view)
115159
BaseStructure typeInfoBase = BaseStructure(TypeInfoType(view), 0);
116160
structureBuilder.SetBaseStructures({typeInfoBase});
117161
// TODO: This exists because if you have no members but a base struct things get screwy.
118-
structureBuilder.SetWidth(0x10);
162+
structureBuilder.SetWidth(TypeInfoSize(view));
119163

120164
Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
121165
view->DefineType(typeId, QualifiedName("__cxxabiv1::__class_type_info"), structureType);
@@ -134,10 +178,11 @@ Ref<Type> SIClassTypeInfoType(BinaryView *view)
134178
if (typeCache == nullptr)
135179
{
136180
Ref<Architecture> arch = view->GetDefaultArchitecture();
181+
uint64_t baseOffset = TypeInfoSize(view);
137182

138183
StructureBuilder structureBuilder;
139184
Ref<Type> pBaseType = Type::PointerType(arch, Type::VoidType());
140-
structureBuilder.AddMemberAtOffset(pBaseType, "__base_type", 0x10);
185+
structureBuilder.AddMemberAtOffset(pBaseType, "__base_type", baseOffset);
141186
BaseStructure classTypeInfoBase = BaseStructure(ClassTypeInfoType(view), 0);
142187
structureBuilder.SetBaseStructures({classTypeInfoBase});
143188

@@ -159,14 +204,15 @@ Ref<Type> OffsetFlagsMasksType(BinaryView *view)
159204
if (typeCache == nullptr)
160205
{
161206
Ref<Architecture> arch = view->GetDefaultArchitecture();
162-
Ref<Type> uintType = Type::IntegerType(4, false);
207+
// u32 on 64 and u16 on 32, see comment in BaseClassTypeInfo::BaseClassTypeInfo
208+
uint64_t enumSize = ArchFieldSize(view);
163209

164210
EnumerationBuilder enumerationBuilder;
165211
enumerationBuilder.AddMemberWithValue("__virtual_mask", 0x1);
166212
enumerationBuilder.AddMemberWithValue("__public_mask", 0x2);
167213
enumerationBuilder.AddMemberWithValue("__offset_shift", 0x8);
168214

169-
Ref<Type> enumerationType = TypeBuilder::EnumerationType(arch, enumerationBuilder.Finalize()).Finalize();
215+
Ref<Type> enumerationType = TypeBuilder::EnumerationType(arch, enumerationBuilder.Finalize(), enumSize).Finalize();
170216
view->DefineType(typeId, QualifiedName("__cxxabiv1::__offset_flags_masks"), enumerationType);
171217

172218
typeCache = view->GetTypeById(typeId);
@@ -184,7 +230,9 @@ Ref<Type> BaseClassTypeInfoType(BinaryView *view)
184230
if (typeCache == nullptr)
185231
{
186232
Ref<Architecture> arch = view->GetDefaultArchitecture();
187-
Ref<Type> uintType = Type::IntegerType(4, false);
233+
// u32 on 64 and u16 on 32, see comment in BaseClassTypeInfo::BaseClassTypeInfo
234+
uint64_t fieldSize = ArchFieldSize(view);
235+
Ref<Type> uintType = Type::IntegerType(fieldSize, false);
188236

189237
StructureBuilder structureBuilder;
190238
Ref<Type> pBaseType = Type::PointerType(arch, Type::VoidType());
@@ -230,12 +278,13 @@ Ref<Type> VMIClassTypeInfoType(BinaryView *view, uint64_t baseCount)
230278
{
231279
Ref<Architecture> arch = view->GetDefaultArchitecture();
232280
Ref<Type> uintType = Type::IntegerType(4, false);
281+
uint64_t baseOffset = TypeInfoSize(view);
233282

234283
StructureBuilder structureBuilder;
235-
structureBuilder.AddMemberAtOffset(VMIFlagsMasksType(view), "__flags", 0x10);
236-
structureBuilder.AddMemberAtOffset(uintType, "__base_count", 0x14);
284+
structureBuilder.AddMemberAtOffset(VMIFlagsMasksType(view), "__flags", baseOffset);
285+
structureBuilder.AddMemberAtOffset(uintType, "__base_count", baseOffset + 0x4);
237286
Ref<Type> baseInfoType = Type::ArrayType(BaseClassTypeInfoType(view), baseCount);
238-
structureBuilder.AddMemberAtOffset(baseInfoType, "__base_info", 0x18);
287+
structureBuilder.AddMemberAtOffset(baseInfoType, "__base_info", baseOffset + 0x8);
239288
BaseStructure classTypeInfoBase = BaseStructure(ClassTypeInfoType(view), 0);
240289
structureBuilder.SetBaseStructures({classTypeInfoBase});
241290

@@ -248,43 +297,32 @@ std::optional<TypeInfoVariant> ReadTypeInfoVariant(BinaryView *view, uint64_t ob
248297
auto typeInfo = GetTypeInfo(view, objectAddr);
249298
if (!typeInfo.has_value())
250299
return std::nullopt;
251-
252-
// TODO: What if there is no symbol?
253-
// If there is a symbol at objectAddr pointing to a symbol starting with "vtable for __cxxabiv1"
254-
auto baseSym = view->GetSymbolByAddress(typeInfo->base);
255-
if (baseSym == nullptr)
256-
{
257-
// Check relocation at objectAddr for symbol
258-
for (const auto& r : view->GetRelocationsAt(objectAddr))
259-
if (auto relocSym = r->GetSymbol())
260-
baseSym = relocSym;
261-
}
262300

263-
if (baseSym != nullptr && baseSym->GetType() != ExternalSymbol)
264-
{
265-
// The base did have a symbol, but it wasn't external.
266-
// NOTE: We only require it to be external for symbol available bases.
267-
return std::nullopt;
268-
}
301+
// If there is a symbol at objectAddr pointing to a symbol starting with "vtable for __cxxabiv1"
302+
Ref<Symbol> baseSym = GetRealSymbol(view, objectAddr, typeInfo->base);
269303

270304
if (baseSym == nullptr)
271305
{
272306
// Verify first that we can even read a pointer sized value at the base.
273-
if (!view->IsValidOffset(typeInfo->base + view->GetAddressSize()))
307+
if (!view->IsValidOffset(typeInfo->base - view->GetAddressSize()))
274308
return std::nullopt;
275309
// We did not find a symbol for the base.
276310
// Last resort, try and deref to check for static linked c++ rt.
277311
// to get the c++ variant assume we are in a vtable like this
278312
// void* data_102bb4ca0 = _typeinfo_for___cxxabiv1::__class_type_info
279313
// void *data_102bb4ca8 = __cxxabiv1::__class_type_info::~__class_type_info() <--- typeInfo.base points to this.
280314
// void *data_102bb4cb0 = __cxxabiv1::__class_type_info::~__class_type_info()
281-
// Because we are pointing at the start of the vtable, we can just deref again to get the symbol.
315+
// While we could just deref to get the symbol of the vfunc, the symbol might not actually be present for that function yet.
316+
// So instead we will deref to the type info above the supposed vtable.
317+
// TODO: We should really just deref into the type info to get the name string.
282318
BinaryReader reader = BinaryReader(view);
283-
reader.Seek(typeInfo->base);
284-
uint64_t vftAddr = reader.ReadPointer();
285-
if (!view->IsValidOffset(vftAddr))
319+
reader.Seek(typeInfo->base - view->GetAddressSize());
320+
// Read the type info pointer above the vft
321+
uint64_t typeInfoAddr = reader.ReadPointer();
322+
if (!view->IsValidOffset(typeInfoAddr))
286323
return std::nullopt;
287-
auto vftSym = view->GetSymbolByAddress(vftAddr);
324+
LogInfo("Assuming base type info for %llx is at %llx", objectAddr, typeInfoAddr);
325+
auto vftSym = view->GetSymbolByAddress(typeInfoAddr);
288326
if (vftSym == nullptr)
289327
return std::nullopt;
290328
baseSym = vftSym;
@@ -294,21 +332,12 @@ std::optional<TypeInfoVariant> ReadTypeInfoVariant(BinaryView *view, uint64_t ob
294332
if (baseSymName.find("__cxxabiv1") != std::string::npos)
295333
{
296334
// symbol takes the form of `abi::base_name::addend`
297-
// Remove the `abi::`
298-
auto baseTyStartPos = baseSymName.find("::");
299-
if (baseTyStartPos != std::string::npos)
300-
baseSymName = baseSymName.substr(baseTyStartPos + 2);
301-
// Remove the `::addend`
302-
auto baseTyEndPos = baseSymName.find("::");
303-
if (baseTyEndPos != std::string::npos)
304-
baseSymName = baseSymName.substr(0, baseTyEndPos);
305-
306-
if (baseSymName == "__class_type_info")
307-
return TIVClass;
308-
if (baseSymName == "__si_class_type_info")
335+
if (baseSymName.find("si_class_type_info") != std::string::npos)
309336
return TIVSIClass;
310-
if (baseSymName == "__vmi_class_type_info")
337+
if (baseSymName.find("vmi_class_type_info") != std::string::npos)
311338
return TIVVMIClass;
339+
if (baseSymName.find("class_type_info") != std::string::npos)
340+
return TIVClass;
312341
}
313342

314343
return std::nullopt;
@@ -319,8 +348,9 @@ std::optional<BaseClassInfo> ItaniumRTTIProcessor::ProcessVFTBaseClassInfo(uint6
319348
{
320349
BinaryReader reader = BinaryReader(m_view);
321350
// Because we have this we _need_ to have the adjustment stuff.
322-
// NOTE: We assume two 0x4 ints with the first being what we want.
323-
reader.Seek(vftAddr - 0x10);
351+
// NOTE: We assume two field sized ints with the first being what we want.
352+
// Two 0x4 ints and the pointer to the type info.
353+
reader.Seek(vftAddr - ((ArchFieldSize(m_view) * 2) + m_view->GetAddressSize()));
324354

325355
auto adjustmentOffset = static_cast<int32_t>(reader.Read32());
326356
[[maybe_unused]]
@@ -331,15 +361,6 @@ std::optional<BaseClassInfo> ItaniumRTTIProcessor::ProcessVFTBaseClassInfo(uint6
331361
// Assuming we do not have a baseClassInfo already passed we can deduce it here.
332362
for (auto& baseClass : classInfo.baseClasses)
333363
{
334-
// if (baseClass.offset == 0)
335-
// {
336-
// // If the base class is at offset 0 that means it has yet to be adjusted.
337-
// // NOTE: This should only happen for `TIVSIClass`. If this assigns more than
338-
// // one base class to this offset we are screwed.
339-
// baseClass.offset = classOffset;
340-
// LogInfo("Adjusting base class offset for %llx to %llx", vftAddr, classOffset);
341-
// }
342-
343364
if (baseClass.offset == classOffset)
344365
{
345366
// Found the appropriate base class for this vtable.
@@ -436,7 +457,8 @@ std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
436457
return std::nullopt;
437458
m_logger->LogDebug("Non-backed external subtype for %llx", objectAddr);
438459
subTypeName = externTypeName.value();
439-
} else
460+
}
461+
else
440462
{
441463
auto baseTypeInfo = TypeInfo(m_view, baseInfo.base_type);
442464
subTypeName = baseTypeInfo.type_name;
@@ -480,7 +502,8 @@ std::optional<VirtualFunctionTableInfo> ItaniumRTTIProcessor::ProcessVFT(uint64_
480502
{
481503
// TODO: Sometimes vFunc idx will be zeroed iirc.
482504
// We allow vfuncs to point to extern functions.
483-
auto vFuncSym = m_view->GetSymbolByAddress(vFuncAddr);
505+
// TODO: Until https://github.com/Vector35/binaryninja-api/issues/5982 is fixed we need to check extern sym relocs instead of the symbol directly
506+
auto vFuncSym = GetRealSymbol(m_view, reader.GetOffset(), vFuncAddr);
484507
if (!vFuncSym)
485508
break;
486509
DataVariable dv;
@@ -639,14 +662,20 @@ void ItaniumRTTIProcessor::ProcessRTTI()
639662
{
640663
auto start_time = std::chrono::high_resolution_clock::now();
641664
auto addrSize = m_view->GetAddressSize();
642-
// TODO: This probably needs to change
643-
uint64_t maxTypeInfoSize = 0x10;
665+
uint64_t maxTypeInfoSize = TypeInfoSize(m_view);
644666

645667
auto scan = [&](const Ref<Section> &section) {
646668
for (uint64_t currAddr = section->GetStart(); currAddr <= section->GetEnd() - maxTypeInfoSize; currAddr += addrSize)
647669
{
648-
if (auto classInfo = ProcessRTTI(currAddr))
649-
m_classInfo[currAddr] = classInfo.value();
670+
try
671+
{
672+
if (auto classInfo = ProcessRTTI(currAddr))
673+
m_classInfo[currAddr] = classInfo.value();
674+
}
675+
catch (std::exception& e)
676+
{
677+
m_logger->LogWarn("Failed to process object at %llx... skipping", currAddr);
678+
}
650679
}
651680
};
652681

@@ -724,8 +753,8 @@ void ItaniumRTTIProcessor::ProcessVFT()
724753
DataVariable dv;
725754
if (m_view->GetDataVariableAtAddress(ref, dv) && m_classInfo.find(dv.address) != m_classInfo.end())
726755
continue;
727-
// Verify that there is two 4 byte values above the type info pointer
728-
optReader.Seek(ref - 8);
756+
// Verify that there is two field sized values above the type info pointer
757+
optReader.Seek(ref - ArchFieldSize(m_view) * 2);
729758
auto beforeTypeInfoRef = optReader.ReadPointer();
730759
if (m_view->IsValidOffset(beforeTypeInfoRef))
731760
continue;
@@ -737,42 +766,6 @@ void ItaniumRTTIProcessor::ProcessVFT()
737766
}
738767
}
739768

740-
if (virtualFunctionTableSweep)
741-
{
742-
auto addrSize = m_view->GetAddressSize();
743-
auto scan = [&](const Ref<Segment> &segment) {
744-
uint64_t startAddr = segment->GetStart();
745-
uint64_t endAddr = segment->GetEnd();
746-
for (uint64_t vtableAddr = startAddr; vtableAddr < endAddr - 0x10; vtableAddr += addrSize)
747-
{
748-
optReader.Seek(vtableAddr);
749-
uint64_t coLocatorAddr = optReader.ReadPointer();
750-
auto coLocator = m_classInfo.find(coLocatorAddr);
751-
if (coLocator == m_classInfo.end())
752-
continue;
753-
// Found a vtable reference to colocator.
754-
// vftMap[coLocatorAddr] = vtableAddr + addrSize;
755-
}
756-
};
757-
758-
// Scan data sections for virtual function tables.
759-
auto rdataSection = m_view->GetSectionByName(".rdata");
760-
for (const Ref<Segment> &segment: m_view->GetSegments())
761-
{
762-
if (segment->GetFlags() == (SegmentReadable | SegmentContainsData))
763-
{
764-
m_logger->LogDebug("Attempting to find VirtualFunctionTables in segment %llx", segment->GetStart());
765-
scan(segment);
766-
}
767-
else if (checkWritableRData && rdataSection && rdataSection->GetStart() == segment->GetStart())
768-
{
769-
m_logger->LogDebug("Attempting to find VirtualFunctionTables in writable rdata segment %llx",
770-
segment->GetStart());
771-
scan(segment);
772-
}
773-
}
774-
}
775-
776769
auto GetCachedVFTInfo = [&](uint64_t vftAddr, ClassInfo& classInfo) {
777770
// Check in the cache so that we don't process vfts more than once.
778771
auto cachedVftInfo = vftFinishedMap.find(vftAddr);

0 commit comments

Comments
 (0)