Skip to content

Commit 9ff2b8d

Browse files
committed
Itanium RTTI scaffolding
1 parent 7d66d87 commit 9ff2b8d

File tree

9 files changed

+761
-39
lines changed

9 files changed

+761
-39
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ struct _RTTICompleteObjectLocator MapTrackView::`RTTI Complete Object Locator'{f
1818
}
1919
```
2020
21-
_The above listing includes type information deduced seperately through demangled names_
21+
_The above listing includes type information deduced separately through demangled names_
2222
2323
## Example Virtual Function Table Listing
2424
@@ -38,7 +38,7 @@ struct QPaintDevice::MapTrackView::VTable MapTrackView::`vftable'{for `QPaintDev
3838
}
3939
```
4040

41-
_The above listing includes type information deduced seperately through demangled names_
41+
_The above listing includes type information deduced separately through demangled names_
4242

4343
## Exposed Metadata
4444

plugins/rtti/itanium.cpp

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
#include "itanium.h"
2+
3+
using namespace BinaryNinja;
4+
using namespace BinaryNinja::RTTI;
5+
using namespace BinaryNinja::RTTI::Itanium;
6+
7+
// TODO: Need to add the boiler plate stuff
8+
// TODO: Can we find the object offset for the vtable entry?
9+
// TODO: Itanium doesnt really say anything about the sizing of these fields, i assume they are all u32 for thje most part.
10+
11+
constexpr const char *TYPE_SOURCE_ITANIUM = "rtti_itanium";
12+
13+
TypeInfo::TypeInfo(BinaryView *view, uint64_t address)
14+
{
15+
BinaryReader reader = BinaryReader(view);
16+
reader.Seek(address);
17+
base = reader.ReadPointer();
18+
auto typeNameAddr = reader.ReadPointer();
19+
reader.Seek(typeNameAddr);
20+
type_name = reader.ReadCString(512);
21+
}
22+
23+
24+
SIClassTypeInfo::SIClassTypeInfo(BinaryView *view, uint64_t address) : ClassTypeInfo(view, address)
25+
{
26+
BinaryReader reader = BinaryReader(view);
27+
// TODO: Manually seeking to the offset is ugly.
28+
reader.Seek(address + 0x10);
29+
base_type = reader.ReadPointer();
30+
}
31+
32+
33+
BaseClassTypeInfo::BaseClassTypeInfo(BinaryView *view, uint64_t address)
34+
{
35+
BinaryReader reader = BinaryReader(view);
36+
reader.Seek(address);
37+
base_type = reader.ReadPointer();
38+
offset_flags = reader.Read32();
39+
// TODO: Test this...
40+
offset_flags_masks = static_cast<OffsetFlagsMasks>(reader.Read32());
41+
}
42+
43+
44+
VMIClassTypeInfo::VMIClassTypeInfo(BinaryView *view, uint64_t address) : ClassTypeInfo(view, address)
45+
{
46+
BinaryReader reader = BinaryReader(view);
47+
// TODO: Manually seeking to the offset is ugly.
48+
reader.Seek(address + 0x10);
49+
flags = reader.Read32();
50+
base_count = reader.Read32();
51+
base_info = {};
52+
for (size_t i = 1; i < base_count; i++)
53+
{
54+
// TODO: Verify this is correct.
55+
uint64_t currentBaseAddr = reader.GetOffset();
56+
base_info.emplace_back(view, reader.GetOffset());
57+
reader.Seek(currentBaseAddr + 12);
58+
}
59+
}
60+
61+
62+
Ref<Type> TypeInfoType(BinaryView *view)
63+
{
64+
auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("TypeInfo"));
65+
Ref<Type> typeCache = view->GetTypeById(typeId);
66+
67+
if (typeCache == nullptr)
68+
{
69+
Ref<Architecture> arch = view->GetDefaultArchitecture();
70+
71+
StructureBuilder structureBuilder;
72+
Ref<Type> pBaseType = Type::PointerType(arch, Type::VoidType());
73+
structureBuilder.AddMember(pBaseType, "__base");
74+
Ref<Type> pTypeNameType = Type::PointerType(arch, Type::IntegerType(1, true, "char"));
75+
structureBuilder.AddMember(pTypeNameType, "__type_name");
76+
77+
Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
78+
// TODO: std::type_info or __cxxabiv1::__type_info ?
79+
view->DefineType(typeId, QualifiedName("std::type_info"), structureType);
80+
81+
typeCache = view->GetTypeById(typeId);
82+
}
83+
84+
return typeCache;
85+
}
86+
87+
88+
Ref<Type> ClassTypeInfoType(BinaryView *view)
89+
{
90+
auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("ClassTypeInfo"));
91+
Ref<Type> typeCache = view->GetTypeById(typeId);
92+
93+
if (typeCache == nullptr)
94+
{
95+
StructureBuilder structureBuilder;
96+
BaseStructure typeInfoBase = BaseStructure(TypeInfoType(view), 0);
97+
structureBuilder.SetBaseStructures({typeInfoBase});
98+
// TODO: This exists because if you have no members but a base struct things get screwy.
99+
structureBuilder.SetWidth(0x10);
100+
101+
Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
102+
view->DefineType(typeId, QualifiedName("__cxxabiv1::__class_type_info"), structureType);
103+
104+
typeCache = view->GetTypeById(typeId);
105+
}
106+
107+
return typeCache;
108+
}
109+
110+
Ref<Type> SIClassTypeInfoType(BinaryView *view)
111+
{
112+
auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("SIClassTypeInfo"));
113+
Ref<Type> typeCache = view->GetTypeById(typeId);
114+
115+
if (typeCache == nullptr)
116+
{
117+
Ref<Architecture> arch = view->GetDefaultArchitecture();
118+
119+
StructureBuilder structureBuilder;
120+
Ref<Type> pBaseType = Type::PointerType(arch, Type::VoidType());
121+
structureBuilder.AddMemberAtOffset(pBaseType, "__base_type", 0x10);
122+
BaseStructure classTypeInfoBase = BaseStructure(ClassTypeInfoType(view), 0);
123+
structureBuilder.SetBaseStructures({classTypeInfoBase});
124+
125+
Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
126+
view->DefineType(typeId, QualifiedName("__cxxabiv1::__si_class_type_info"), structureType);
127+
128+
typeCache = view->GetTypeById(typeId);
129+
}
130+
131+
return typeCache;
132+
}
133+
134+
135+
Ref<Type> OffsetFlagsMasksType(BinaryView *view)
136+
{
137+
auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("OffsetFlagsMasks"));
138+
Ref<Type> typeCache = view->GetTypeById(typeId);
139+
140+
if (typeCache == nullptr)
141+
{
142+
Ref<Architecture> arch = view->GetDefaultArchitecture();
143+
Ref<Type> uintType = Type::IntegerType(4, false);
144+
145+
EnumerationBuilder enumerationBuilder;
146+
enumerationBuilder.AddMemberWithValue("__virtual_mask", 0x1);
147+
enumerationBuilder.AddMemberWithValue("__public_mask", 0x2);
148+
enumerationBuilder.AddMemberWithValue("__offset_shift", 0x8);
149+
150+
Ref<Type> enumerationType = TypeBuilder::EnumerationType(arch, enumerationBuilder.Finalize()).Finalize();
151+
view->DefineType(typeId, QualifiedName("__cxxabiv1::__offset_flags_masks"), enumerationType);
152+
153+
typeCache = view->GetTypeById(typeId);
154+
}
155+
156+
return typeCache;
157+
}
158+
159+
160+
Ref<Type> BaseClassTypeInfoType(BinaryView *view)
161+
{
162+
auto typeId = Type::GenerateAutoTypeId(TYPE_SOURCE_ITANIUM, QualifiedName("BaseClassTypeInfo"));
163+
Ref<Type> typeCache = view->GetTypeById(typeId);
164+
165+
if (typeCache == nullptr)
166+
{
167+
Ref<Architecture> arch = view->GetDefaultArchitecture();
168+
Ref<Type> uintType = Type::IntegerType(4, false);
169+
170+
StructureBuilder structureBuilder;
171+
Ref<Type> pBaseType = Type::PointerType(arch, Type::VoidType());
172+
structureBuilder.AddMember(pBaseType, "__base_type");
173+
structureBuilder.AddMember(uintType, "__offset_flags");
174+
structureBuilder.AddMember(OffsetFlagsMasksType(view), "__offset_flags_masks");
175+
176+
Ref<Type> structureType = TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
177+
view->DefineType(typeId, QualifiedName("__cxxabiv1::__base_class_type_info"), structureType);
178+
179+
typeCache = view->GetTypeById(typeId);
180+
}
181+
182+
return typeCache;
183+
}
184+
185+
186+
Ref<Type> VMIClassTypeInfoType(BinaryView *view, int baseCount)
187+
{
188+
Ref<Architecture> arch = view->GetDefaultArchitecture();
189+
Ref<Type> uintType = Type::IntegerType(4, false);
190+
191+
StructureBuilder structureBuilder;
192+
structureBuilder.AddMemberAtOffset(uintType, "__flags", 0x10);
193+
structureBuilder.AddMemberAtOffset(uintType, "__base_count", 0x14);
194+
Ref<Type> baseInfoType = Type::ArrayType(BaseClassTypeInfoType(view), baseCount);
195+
structureBuilder.AddMemberAtOffset(baseInfoType, "__base_info", 0x18);
196+
BaseStructure classTypeInfoBase = BaseStructure(ClassTypeInfoType(view), 0);
197+
structureBuilder.SetBaseStructures({classTypeInfoBase});
198+
199+
return TypeBuilder::StructureType(structureBuilder.Finalize()).Finalize();
200+
}
201+
202+
203+
std::optional<TypeInfoVariant> ReadTypeInfoVariant(BinaryView *view, uint64_t objectAddr)
204+
{
205+
auto typeInfo = TypeInfo(view, objectAddr);
206+
207+
// TODO: What if there is no symbol?
208+
// If there is a symbol at objectAddr pointing to a symbol starting with "vtable for __cxxabiv1"
209+
auto baseSym = view->GetSymbolByAddress(typeInfo.base);
210+
if (baseSym == nullptr)
211+
return std::nullopt;
212+
if (baseSym->GetType() != ExternalSymbol)
213+
return std::nullopt;
214+
auto baseSymName = baseSym->GetShortName();
215+
216+
// TODO: __vmi_class_type_info seems to point to operator delete(void*)
217+
// TODO: For now we just bruteforce it with the type_name check...
218+
219+
if (baseSymName.find("__cxxabiv1") != std::string::npos)
220+
{
221+
// symbol takes the form of `abi::base_name`
222+
auto baseTyStartPos = baseSymName.find("::");
223+
if (baseTyStartPos != std::string::npos)
224+
baseSymName = baseSymName.substr(baseTyStartPos + 2);
225+
226+
if (baseSymName == "__class_type_info")
227+
return TIVClass;
228+
if (baseSymName == "__si_class_type_info")
229+
return TIVSIClass;
230+
if (baseSymName == "__vmi_class_type_info")
231+
return TIVVMIClass;
232+
}
233+
else if (typeInfo.type_name.length() > 2)
234+
{
235+
// TODO: This is so ugly
236+
switch (typeInfo.type_name.at(0))
237+
{
238+
case '7':
239+
return TIVClass;
240+
case '9':
241+
return TIVSIClass;
242+
case '1':
243+
if (typeInfo.type_name.at(1) == '4')
244+
return TIVVMIClass;
245+
default:
246+
return std::nullopt;
247+
}
248+
}
249+
250+
return std::nullopt;
251+
}
252+
253+
254+
std::optional<ClassInfo> ItaniumRTTIProcessor::ProcessRTTI(uint64_t objectAddr)
255+
{
256+
// TODO: You cant get subobject offsets from rtti, its stored above this ptr in vtable.
257+
// Get object as type info then check to see if it's valid.
258+
auto typeInfoVariant = ReadTypeInfoVariant(m_view, objectAddr);
259+
if (!typeInfoVariant.has_value())
260+
return std::nullopt;
261+
262+
auto typeInfo = TypeInfo(m_view, objectAddr);
263+
auto className = DemangleNameGNU3(m_view, allowMangledClassNames, typeInfo.type_name);
264+
if (!className.has_value())
265+
return std::nullopt;
266+
auto classInfo = ClassInfo{className.value()};
267+
268+
// TODO: className starts with 7, 9, 14
269+
// 7 == class_type
270+
// 9 == si_class_type
271+
// 14 == vmi_class_type
272+
273+
auto typeInfoName = fmt::format("_typeinfo_for_{}", classInfo.className);
274+
m_view->DefineAutoSymbol(new Symbol{DataSymbol, typeInfoName, objectAddr});
275+
276+
if (typeInfoVariant == TIVSIClass)
277+
{
278+
// Read the base class.
279+
auto siClassTypeInfo = SIClassTypeInfo(m_view, objectAddr);
280+
auto subTypeInfoVariant = ReadTypeInfoVariant(m_view, siClassTypeInfo.base_type);
281+
if (!subTypeInfoVariant.has_value())
282+
return std::nullopt;
283+
auto subTypeInfo = TypeInfo(m_view, siClassTypeInfo.base_type);
284+
// Demangle base class name and set
285+
auto baseClassName = DemangleNameGNU3(m_view, allowMangledClassNames, subTypeInfo.type_name);
286+
if (!baseClassName.has_value())
287+
{
288+
m_logger->LogWarn("Skipping base class with mangled name %llx", siClassTypeInfo.base_type);
289+
return std::nullopt;
290+
}
291+
classInfo.baseClassName = baseClassName;
292+
m_view->DefineDataVariable(objectAddr, Confidence(SIClassTypeInfoType(m_view), 255));
293+
}
294+
else if (typeInfoVariant == TIVVMIClass)
295+
{
296+
// TODO: Read multiple base classes.
297+
auto vmiClassTypeInfo = VMIClassTypeInfo(m_view, objectAddr);
298+
m_view->DefineDataVariable(objectAddr, Confidence(VMIClassTypeInfoType(m_view, vmiClassTypeInfo.base_count), 255));
299+
}
300+
else
301+
{
302+
// auto classTypeInfo = ClassTypeInfo(m_view, objectAddr);
303+
m_view->DefineDataVariable(objectAddr, Confidence(ClassTypeInfoType(m_view), 255));
304+
}
305+
306+
return classInfo;
307+
}
308+
309+
310+
ItaniumRTTIProcessor::ItaniumRTTIProcessor(const Ref<BinaryView> &view, bool useMangled, bool checkRData, bool vftSweep) : m_view(view)
311+
{
312+
m_logger = new Logger("Itanium RTTI");
313+
allowMangledClassNames = useMangled;
314+
checkWritableRData = checkRData;
315+
m_classInfo = {};
316+
virtualFunctionTableSweep = vftSweep;
317+
318+
auto metadata = view->QueryMetadata(VIEW_METADATA_RTTI);
319+
if (metadata != nullptr)
320+
{
321+
// TODO: This will pull in microsoft RTTI, which is really weird behavior possibly.
322+
// Load in metadata to the processor.
323+
// DeserializedMetadata(metadata);
324+
}
325+
}
326+
327+
328+
void ItaniumRTTIProcessor::ProcessRTTI()
329+
{
330+
auto start_time = std::chrono::high_resolution_clock::now();
331+
auto addrSize = m_view->GetAddressSize();
332+
// TODO: This probably needs to change
333+
uint64_t maxTypeInfoSize = 0x10;
334+
335+
auto scan = [&](const Ref<Section> &section) {
336+
for (uint64_t currAddr = section->GetStart(); currAddr <= section->GetEnd() - maxTypeInfoSize; currAddr += addrSize)
337+
{
338+
if (auto classInfo = ProcessRTTI(currAddr))
339+
m_classInfo[currAddr] = classInfo.value();
340+
}
341+
};
342+
343+
// Scan data sections for rtti.
344+
for (const Ref<Section> &section: m_view->GetSections())
345+
{
346+
if (section->GetSemantics() == ReadOnlyDataSectionSemantics)
347+
{
348+
m_logger->LogDebug("Attempting to find RTTI in section %llx", section->GetStart());
349+
scan(section);
350+
}
351+
}
352+
353+
auto end_time = std::chrono::high_resolution_clock::now();
354+
std::chrono::duration<double> elapsed_time = end_time - start_time;
355+
m_logger->LogInfo("ProcessRTTI took %f seconds", elapsed_time.count());
356+
}

0 commit comments

Comments
 (0)