Skip to content

Commit

Permalink
Add classes for finding RTTI on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
danielkrupinski committed Dec 15, 2023
1 parent 7c35978 commit a88ca48
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Source/Osiris.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@
<ClInclude Include="Platform\Windows\DLLs\Shell32Dll.h" />
<ClInclude Include="Platform\Windows\PebLdr.h" />
<ClInclude Include="Platform\Windows\PortableExecutable.h" />
<ClInclude Include="Platform\Windows\RTTI\RttiCompleteObjectLocator.h" />
<ClInclude Include="Platform\Windows\RTTI\RttiCompleteObjectLocatorFinder.h" />
<ClInclude Include="Platform\Windows\RTTI\RttiTypeDescriptor.h" />
<ClInclude Include="Platform\Windows\RTTI\RttiTypeDescriptorFinder.h" />
<ClInclude Include="Platform\Windows\RTTI\ToRvaConverter.h" />
<ClInclude Include="Platform\Windows\UserDocumentsFolderPath.h" />
<ClInclude Include="Platform\Windows\Win.h" />
<ClInclude Include="Platform\Windows\WindowsDynamicLibrary.h" />
Expand Down
18 changes: 18 additions & 0 deletions Source/Osiris.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@
<Filter Include="FeatureHelpers\Hud">
<UniqueIdentifier>{7a55ce58-0ac3-4a7f-817d-46121c678167}</UniqueIdentifier>
</Filter>
<Filter Include="Platform\Windows\RTTI">
<UniqueIdentifier>{3908a784-03e1-437a-97b7-7479673278cc}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CS2\Classes\C_CSGameRules.h">
Expand Down Expand Up @@ -741,6 +744,21 @@
<ClInclude Include="MemorySearch\BinaryBytePattern.h">
<Filter>MemorySearch</Filter>
</ClInclude>
<ClInclude Include="Platform\Windows\RTTI\RttiCompleteObjectLocatorFinder.h">
<Filter>Platform\Windows\RTTI</Filter>
</ClInclude>
<ClInclude Include="Platform\Windows\RTTI\RttiTypeDescriptor.h">
<Filter>Platform\Windows\RTTI</Filter>
</ClInclude>
<ClInclude Include="Platform\Windows\RTTI\RttiTypeDescriptorFinder.h">
<Filter>Platform\Windows\RTTI</Filter>
</ClInclude>
<ClInclude Include="Platform\Windows\RTTI\ToRvaConverter.h">
<Filter>Platform\Windows\RTTI</Filter>
</ClInclude>
<ClInclude Include="Platform\Windows\RTTI\RttiCompleteObjectLocator.h">
<Filter>Platform\Windows\RTTI</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="UI\Panorama\CreateGUI.js">
Expand Down
6 changes: 6 additions & 0 deletions Source/Platform/Windows/RTTI/RttiCompleteObjectLocator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

struct RttiCompleteObjectLocator {
static constexpr auto kOffsetOfOffsetInCompleteClass{4};
static constexpr auto kOffsetOfTypeDescriptorRva{12};
};
55 changes: 55 additions & 0 deletions Source/Platform/Windows/RTTI/RttiCompleteObjectLocatorFinder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#pragma once

#include <cstdint>
#include <cstring>

#include "RttiCompleteObjectLocator.h"
#include "ToRvaConverter.h"
#include <Utils/MemorySection.h>

struct RttiTypeDescriptor;

class RttiCompleteObjectLocatorFinder {
public:
RttiCompleteObjectLocatorFinder(MemorySection rdataSection, ToRvaConverter toRvaConverter) noexcept
: rdataSection{rdataSection}
, toRvaConverter{toRvaConverter}
{
}

[[nodiscard]] const RttiCompleteObjectLocator* findCompleteObjectLocator(const RttiTypeDescriptor* typeDescriptor) const noexcept
{
const BinaryBytePattern typeDescriptorRvaPattern{toRvaConverter.toRva(reinterpret_cast<std::uintptr_t>(typeDescriptor))};
HybridPatternFinder typeDescriptorCrossReferenceFinder{rdataSection.raw(), typeDescriptorRvaPattern};

auto typeDescriptorReference{typeDescriptorCrossReferenceFinder.findNextOccurrence()};
while (typeDescriptorReference && (!isCompleteObjectLocator(reinterpret_cast<std::uintptr_t>(typeDescriptorReference)) || !isCompleteObjectLocatorOfCompleteClass(typeDescriptorReference)))
typeDescriptorReference = typeDescriptorCrossReferenceFinder.findNextOccurrence();

if (typeDescriptorReference)
return reinterpret_cast<const RttiCompleteObjectLocator*>(typeDescriptorReference - RttiCompleteObjectLocator::kOffsetOfTypeDescriptorRva);

return nullptr;
}

private:
[[nodiscard]] bool isCompleteObjectLocatorOfCompleteClass(const std::byte* typeDescriptorReference) const noexcept
{
std::uint32_t offsetInCompleteClass;
std::memcpy(&offsetInCompleteClass, typeDescriptorReference - RttiCompleteObjectLocator::kOffsetOfTypeDescriptorRva + RttiCompleteObjectLocator::kOffsetOfOffsetInCompleteClass, sizeof(std::uint32_t));
return offsetInCompleteClass == 0;
}

[[nodiscard]] bool isCompleteObjectLocator(std::uintptr_t typeDescriptorReference) const noexcept
{
return hasNoCrossReferences(BinaryBytePattern{toRvaConverter.toRva(typeDescriptorReference)}) && rdataSection.offsetOf(typeDescriptorReference) >= RttiCompleteObjectLocator::kOffsetOfTypeDescriptorRva;
}

[[nodiscard]] bool hasNoCrossReferences(BytePattern pattern) const noexcept
{
return HybridPatternFinder{rdataSection.raw(), pattern}.findNextOccurrence() == nullptr;
}

MemorySection rdataSection;
ToRvaConverter toRvaConverter;
};
5 changes: 5 additions & 0 deletions Source/Platform/Windows/RTTI/RttiTypeDescriptor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

struct RttiTypeDescriptor {
static constexpr auto kOffsetOfName{16};
};
42 changes: 42 additions & 0 deletions Source/Platform/Windows/RTTI/RttiTypeDescriptorFinder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <string_view>

#include <MemorySearch/HybridPatternFinder.h>
#include "RttiTypeDescriptor.h"
#include <Utils/MemorySection.h>

class RttiTypeDescriptorFinder {
public:
RttiTypeDescriptorFinder(MemorySection dataSection) noexcept
: dataSection{dataSection}
{
}

[[nodiscard]] const RttiTypeDescriptor* findTypeDescriptor(std::string_view mangledTypeName) const noexcept
{
if (const auto typeDescriptorName{findTypeDescriptorName(mangledTypeName)}; canBePartOfTypeDescriptor(typeDescriptorName))
return toTypeDescriptorPointer(typeDescriptorName);
return nullptr;
}

private:
[[nodiscard]] const std::byte* findTypeDescriptorName(std::string_view typeDescriptorName) const noexcept
{
return HybridPatternFinder{dataSection.raw(), BytePattern{typeDescriptorName}}.findNextOccurrence();
}

[[nodiscard]] bool canBePartOfTypeDescriptor(const std::byte* typeDescriptorName) const noexcept
{
return typeDescriptorName && dataSection.offsetOf(reinterpret_cast<std::uintptr_t>(typeDescriptorName)) >= RttiTypeDescriptor::kOffsetOfName;
}

[[nodiscard]] static const RttiTypeDescriptor* toTypeDescriptorPointer(const std::byte* typeDescriptorName) noexcept
{
return reinterpret_cast<const RttiTypeDescriptor*>(typeDescriptorName - RttiTypeDescriptor::kOffsetOfName);
}

MemorySection dataSection;
};
14 changes: 14 additions & 0 deletions Source/Platform/Windows/RTTI/ToRvaConverter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <cassert>
#include <cstdint>

struct ToRvaConverter {
[[nodiscard]] std::uint32_t toRva(std::uintptr_t address) const noexcept
{
assert(address >= baseAddress);
return static_cast<std::uint32_t>(address - baseAddress);
}

std::uintptr_t baseAddress;
};
6 changes: 6 additions & 0 deletions Source/Utils/MemorySection.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ struct MemorySection {
return address >= base && address - base < size;
}

[[nodiscard]] std::size_t offsetOf(std::uintptr_t address) const noexcept
{
assert(contains(address));
return address - base;
}

private:
std::uintptr_t base{0};
std::size_t size{0};
Expand Down

0 comments on commit a88ca48

Please sign in to comment.