Skip to content

Instantly share code, notes, and snippets.

@altalk23
Last active June 19, 2022 00:56
Show Gist options
  • Save altalk23/85598a67d2bb1c95bafd0d95fc7b98bd to your computer and use it in GitHub Desktop.
Save altalk23/85598a67d2bb1c95bafd0d95fc7b98bd to your computer and use it in GitHub Desktop.
typeinfo cast for multiple inheritance using typeid name comparison
#include <type_traits>
#include <typeinfo>
#include <cstring>
#if __x86_64__
using uinthalf_t = uint32_t;
using inthalf_t = int32_t;
#else
using uinthalf_t = uint16_t;
using inthalf_t = int16_t;
#endif
struct DummyClass {
virtual ~DummyClass() {}
};
struct DummySingleClass : DummyClass {};
struct DummyClass2 {};
struct DummyMultipleClass : DummySingleClass, DummyClass2 {};
struct ClassTypeinfoType {
void** m_typeinfoVtable;
char const* m_typeinfoName;
};
struct SingleClassTypeinfoType : ClassTypeinfoType {
ClassTypeinfoType* m_baseClassTypeinfo;
};
#pragma pack(push, 1)
struct MultipleClassSingleEntryType {
ClassTypeinfoType* m_baseClassTypeinfo;
uint8_t m_visibilityFlag;
inthalf_t m_offset;
uint8_t m_padding[sizeof(inthalf_t) - 1];
};
#pragma pack(pop)
struct MultipleClassTypeinfoType : ClassTypeinfoType {
uint32_t m_flags;
uint32_t m_numBaseClass;
MultipleClassSingleEntryType m_baseClasses[0x100];
};
struct VtableTypeinfoType {
inthalf_t m_offset;
ClassTypeinfoType* m_typeinfo;
};
struct VtableType {
void* m_vtable[0x100];
};
struct CompleteVtableType : VtableTypeinfoType, VtableType {};
void** typeinfoVtableOf(void* ptr) {
auto vftable = *reinterpret_cast<VtableType**>(ptr);
auto typeinfoPtr = static_cast<VtableTypeinfoType*>(static_cast<CompleteVtableType*>(vftable));
return typeinfoPtr->m_typeinfo->m_typeinfoVtable;
}
void* traverseTypeinfoFor(void* ptr, ClassTypeinfoType* typeinfo, char const* afterIdent) {
DummySingleClass dummySingleClass;
DummyMultipleClass dummyMultipleClass;
{
auto optionIdent = typeinfo->m_typeinfoName;
if (std::strcmp(optionIdent, afterIdent) == 0) {
return ptr;
}
}
if (typeinfo->m_typeinfoVtable == typeinfoVtableOf(&dummySingleClass)) {
auto siTypeinfo = static_cast<SingleClassTypeinfoType*>(typeinfo);
return traverseTypeinfoFor(ptr, siTypeinfo->m_baseClassTypeinfo, afterIdent);
}
else if (typeinfo->m_typeinfoVtable == typeinfoVtableOf(&dummyMultipleClass)) {
auto vmiTypeinfo = static_cast<MultipleClassTypeinfoType*>(typeinfo);
for (int i = 0; i < vmiTypeinfo->m_numBaseClass; ++i) {
auto& entry = vmiTypeinfo->m_baseClasses[i];
auto optionPtr = reinterpret_cast<std::byte*>(ptr) + entry.m_offset;
auto ret = traverseTypeinfoFor(optionPtr, entry.m_baseClassTypeinfo, afterIdent);
if (ret != nullptr) return ret;
}
}
return nullptr;
}
template <class After, class Before>
After typeinfo_cast(Before ptr) {
static_assert(
std::is_polymorphic_v<std::remove_pointer_t<Before>>,
"something is not polymorphic"
);
auto basePtr = dynamic_cast<void*>(ptr);
auto vftable = *reinterpret_cast<VtableType**>(basePtr);
auto typeinfo = static_cast<VtableTypeinfoType*>(static_cast<CompleteVtableType*>(vftable))->m_typeinfo;
auto afterTypeinfo = reinterpret_cast<ClassTypeinfoType const*>(&typeid(std::remove_pointer_t<After>));
auto afterIdent = afterTypeinfo->m_typeinfoName;
return static_cast<After>(traverseTypeinfoFor(basePtr, typeinfo, afterIdent));
}
#include <type_traits>
#include <typeinfo>
#include <cstring>
struct TypeDescriptorType {
void* m_typeinfoTable;
int32_t m_spare;
char m_typeDescriptorName[0x100];
};
struct ClassDescriptorType;
struct BaseClassDescriptorType {
TypeDescriptorType* m_typeDescriptor;
int32_t m_numContainedBases;
int32_t m_memberDisplacement[3];
int32_t m_attributes;
ClassDescriptorType* m_classDescriptor;
};
struct ClassDescriptorType {
int32_t m_signature;
int32_t m_attributes;
int32_t m_numBaseClasses;
BaseClassDescriptorType** m_baseClassArray;
};
struct CompleteLocatorType {
int32_t m_signature;
int32_t m_offset;
int32_t m_cdOffset;
TypeDescriptorType* m_typeDescriptor;
ClassDescriptorType* m_classDescriptor;
};
struct MetaPointerType {
CompleteLocatorType* m_completeLocator;
};
struct VftableType {
void* m_vftable[0x100];
};
struct CompleteVftableType : MetaPointerType, VftableType {};
template <class After, class Before>
After typeinfo_cast(Before ptr) {
static_assert(
std::is_polymorphic_v<std::remove_pointer_t<Before>>,
"Input is not a polymorphic type"
);
// i dont think this is even needed but eh
auto basePtr = dynamic_cast<void*>(ptr);
auto vftable = *reinterpret_cast<VftableType**>(basePtr);
auto metaPtr = static_cast<MetaPointerType*>(static_cast<CompleteVftableType*>(vftable));
auto afterDesc = reinterpret_cast<TypeDescriptorType const*>(&typeid(std::remove_pointer_t<After>));
auto afterIdent = static_cast<char const*>(afterDesc->m_typeDescriptorName);
auto classDesc = metaPtr->m_completeLocator->m_classDescriptor;
for (int32_t i = 0; i < classDesc->m_numBaseClasses; ++i) {
auto optionIdent = static_cast<char const*>(classDesc->m_baseClassArray[i]->m_typeDescriptor->m_typeDescriptorName);
auto optionOffset = classDesc->m_baseClassArray[i]->m_memberDisplacement[0];
if (std::strcmp(afterIdent, optionIdent) == 0) {
auto afterPtr = reinterpret_cast<std::byte*>(basePtr) + optionOffset;
return reinterpret_cast<After>(afterPtr);
}
}
return nullptr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment