Skip to content

Instantly share code, notes, and snippets.

@TheLeftExit
Created June 12, 2021 16:06
Show Gist options
  • Save TheLeftExit/8a630f4ebae4a37ae213ca04b270a238 to your computer and use it in GitHub Desktop.
Save TheLeftExit/8a630f4ebae4a37ae213ca04b270a238 to your computer and use it in GitHub Desktop.
Standalone RTTI in C#
using System;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace TheLeftExit.RTTI
{
static class ProcessManipulations
{
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
public static IntPtr CreateHandle(this Process process) =>
OpenProcess(0x0010, false, process.Id);
[DllImport("kernel32.dll")]
private static extern bool ReadProcessMemory(int hProcess, long lpBaseAddress, byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead);
public static bool ReadInt32(this IntPtr handle, long address, out int result)
{
byte[] buffer = new byte[sizeof(int)];
if (!ReadProcessMemory((int)handle, address, buffer, buffer.Length, 0))
{
result = 0;
return false;
}
result = BitConverter.ToInt32(buffer, 0);
return true;
}
public static bool ReadInt64(this IntPtr handle, long address, out long result)
{
byte[] buffer = new byte[sizeof(long)];
if (!ReadProcessMemory((int)handle, address, buffer, buffer.Length, 0))
{
result = 0;
return false;
}
result = BitConverter.ToInt64(buffer, 0);
return true;
}
public static bool ReadString(this IntPtr handle, long address, int maxLength, out string result)
{
byte[] buffer = new byte[maxLength];
if (!ReadProcessMemory((int)handle, address, buffer, buffer.Length, 0))
{
result = null;
return false;
}
result = Encoding.UTF8.GetString(buffer);
return true;
}
public static bool ReadFloat(this IntPtr handle, long address, out float result)
{
byte[] buffer = new byte[sizeof(float)];
if (!ReadProcessMemory((int)handle, address, buffer, buffer.Length, 0))
{
result = 0;
return false;
}
result = BitConverter.ToSingle(buffer, 0);
return true;
}
public static bool ReadDouble(this IntPtr handle, long address, out double result)
{
byte[] buffer = new byte[sizeof(double)];
if (!ReadProcessMemory((int)handle, address, buffer, buffer.Length, 0))
{
result = 0;
return false;
}
result = BitConverter.ToDouble(buffer, 0);
return true;
}
public static bool ReadBytes(this IntPtr handle, long address, int count, byte[] result) =>
ReadProcessMemory((int)handle, address, result, count, 0);
}
static class RTTI
{
[DllImport("dbghelp.dll", CharSet = CharSet.Unicode)]
private static extern int UnDecorateSymbolName(string DecoratedName, StringBuilder UnDecoratedName, int UndecoratedLength, int Flags);
public static string[] GetRTTIClassNames(this IntPtr handle, long address)
{
handle.ReadInt64(address, out long struct_addr);
handle.ReadInt64(struct_addr - IntPtr.Size, out long object_locator_ptr);
handle.ReadInt64(object_locator_ptr + 0x14, out long base_offset);
long base_address = object_locator_ptr - base_offset;
handle.ReadInt32(object_locator_ptr + 0x10, out int class_hierarchy_descriptor_offset);
long class_hierarchy_descriptor_ptr = base_address + class_hierarchy_descriptor_offset;
handle.ReadInt32(class_hierarchy_descriptor_ptr + 0x08, out int base_class_count);
handle.ReadInt32(class_hierarchy_descriptor_ptr + 0x0C, out int base_class_array_offset);
long base_class_array_ptr = base_address + base_class_array_offset;
string[] names = new string[base_class_count];
for (int i = 0; i < base_class_count; i++)
{
handle.ReadInt32(base_class_array_ptr + 4 * i, out int base_class_descriptor_offset);
long base_class_descriptor_ptr = base_address + base_class_descriptor_offset;
handle.ReadInt32(base_class_descriptor_ptr, out int type_descriptor_offset);
long type_descriptor_ptr = base_address + type_descriptor_offset;
handle.ReadString(type_descriptor_ptr + 0x14, 32, out names[i]);
names[i] = names[i].Substring(0, names[i].IndexOf('\0')); // why do i have to
if (names[i].EndsWith("@@"))
{
var sb = new StringBuilder(255);
UnDecorateSymbolName("?" + names[i], sb, sb.Capacity, 0x1000);
names[i] = sb.ToString();
}
}
return names;
}
}
class Program
{
static void Main(string[] args)
{
Process gt = Process.GetProcessesByName("Growtopia").Single();
IntPtr gthandle = gt.CreateHandle();
gthandle.ReadInt64((long)gt.MainModule.BaseAddress + 0x7667F8, out long addr);
gthandle.ReadInt64(addr + 0xAB0, out addr);
gthandle.ReadInt64(addr + 0x198, out addr);
var names = gthandle.GetRTTIClassNames(addr);
; // breakpoint
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment