Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion managed/CounterStrikeSharp.API/Core/API.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1464,7 +1464,7 @@ public static IntPtr FindVirtualTable(string modulepath, string vtablename){
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(modulepath);
ScriptContext.GlobalScriptContext.Push(vtablename);
ScriptContext.GlobalScriptContext.SetIdentifier(0xB4A0F13C);
ScriptContext.GlobalScriptContext.SetIdentifier(0xEA506CFF);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr));
Expand Down
126 changes: 121 additions & 5 deletions src/core/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@
*/

#include "gameconfig.h"
#include "log.h"
#include "memory_module.h"

using namespace counterstrikesharp::modules;

void* FindSignature(const char* moduleName, const char* bytesStr)
{
auto module = counterstrikesharp::modules::GetModuleByName(moduleName);
auto module = GetModuleByName(moduleName);
if (module == nullptr)
{
return nullptr;
Expand All @@ -33,13 +36,126 @@ void* FindSignature(const char* moduleName, const char* bytesStr)
return module->FindSignature(bytesStr);
}

void* FindVirtualTable(const char* moduleName, const char* vtableName)
#ifdef _WIN32
void* GetVirtualTable(CModule* module, const std::string& name)
{
auto module = counterstrikesharp::modules::GetModuleByName(moduleName);
if (module == nullptr)
auto runTimeData = module->GetSection(".data");
auto readOnlyData = module->GetSection(".rdata");

if (!runTimeData || !readOnlyData)
{
CSSHARP_CORE_ERROR("Failed to find .data or .rdata section");
return nullptr;
}

// Windows RTTI format: .?AVClassName@@
std::string decoratedTableName = ".?AV" + name + "@@";

SignatureIterator sigIt(runTimeData->m_pBase, runTimeData->m_iSize, (const byte*)decoratedTableName.c_str(),
decoratedTableName.size() + 1);

void* typeDescriptor = sigIt.FindNext(false);
if (!typeDescriptor)
{
CSSHARP_CORE_ERROR("Failed to find type descriptor for {}", name);
return nullptr;
}

typeDescriptor = (void*)((uintptr_t)typeDescriptor - 0x10);
const uint32_t rttiTDRva = (uintptr_t)typeDescriptor - (uintptr_t)module->m_base;

SignatureIterator sigIt2(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte*)&rttiTDRva, sizeof(uint32_t));

while (void* completeObjectLocator = sigIt2.FindNext(false))
{
auto completeObjectLocatorHeader = (uintptr_t)completeObjectLocator - 0xC;

// Verify RTTI Complete Object Locator header (always 0x1)
if (*(int32_t*)(completeObjectLocatorHeader) != 1) continue;

// Verify RTTI vtable offset (always 0)
if (*(int32_t*)((uintptr_t)completeObjectLocator - 0x8) != 0) continue;

// Find reference to Complete Object Locator inside .rdata
SignatureIterator sigIt3(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte*)&completeObjectLocatorHeader, sizeof(void*));

void* vtable = sigIt3.FindNext(false);
if (!vtable)
{
CSSHARP_CORE_ERROR("Failed to find vtable for {}", name);
return nullptr;
}

// Return pointer after Complete Object Locator
// (vtable + 0x8) → start of first virtual function
return (void*)((uintptr_t)vtable + 0x8);
}

CSSHARP_CORE_ERROR("Failed to find RTTI Complete Object Locator for {}", name);
return nullptr;
}
#else
void* GetVirtualTable(CModule* module, const std::string& name)
{
auto readOnlyData = module->GetSection(".rodata");
auto readOnlyRelocations = module->GetSection(".data.rel.ro");

if (!readOnlyData || !readOnlyRelocations)
{
CSSHARP_CORE_ERROR("Failed to find .rodata or .data.rel.ro section");
return nullptr;
}

// Linux RTTI format: "17CNavPhysicsInterface" etc.
std::string decoratedTableName = std::to_string(name.length()) + name;

SignatureIterator sigIt(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte*)decoratedTableName.c_str(),
decoratedTableName.size() + 1);
void* classNameString = sigIt.FindNext(false);
if (!classNameString)
{
CSSHARP_CORE_ERROR("Failed to find type descriptor for {}", name);
return nullptr;
}

return module->FindVirtualTable(vtableName);
// Find relocation referencing classNameString
SignatureIterator sigIt2(readOnlyRelocations->m_pBase, readOnlyRelocations->m_iSize, (const byte*)&classNameString, sizeof(void*));
void* typeName = sigIt2.FindNext(false);
if (!typeName)
{
CSSHARP_CORE_ERROR("Failed to find type name for {}", name);
return nullptr;
}

void* typeInfo = (void*)((uintptr_t)typeName - 0x8);

// Check both local/global relocation tables
for (const auto& sectionName : { std::string_view(".data.rel.ro"), std::string_view(".data.rel.ro.local") })
{
auto section = module->GetSection(sectionName);
if (!section) continue;

SignatureIterator sigIt3(section->m_pBase, section->m_iSize, (const byte*)&typeInfo, sizeof(void*));

while (void* vtable = sigIt3.FindNext(false))
{
// Verify offset-to-top == 0
if (*(int64_t*)((uintptr_t)vtable - 0x8) == 0)
{
// Return start of actual virtual method table
// (vtable + 0x10) = skip RTTI + offset-to-top
return (void*)((uintptr_t)vtable + 0x10);
}
}
}

CSSHARP_CORE_ERROR("Failed to find vtable for {}", name);
return nullptr;
}
#endif

void* FindVirtualTable(const char* moduleName, const char* vtableName)
{
CModule* module = GetModuleByName(moduleName);
return module ? GetVirtualTable(module, vtableName) : nullptr;
}
Loading