Skip to content

Commit

Permalink
SDK: Add UObjectBase::get_full_name and offset bruteforcing
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Jul 6, 2023
1 parent e030488 commit 5d4af5f
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ list(APPEND sdk_SOURCES
"shared/sdk/UGameEngine.cpp"
"shared/sdk/UGameViewportClient.cpp"
"shared/sdk/UObjectArray.cpp"
"shared/sdk/UObjectBase.cpp"
"shared/sdk/Utility.cpp"
"shared/sdk/CVar.hpp"
"shared/sdk/ConsoleManager.hpp"
Expand All @@ -348,6 +349,7 @@ list(APPEND sdk_SOURCES
"shared/sdk/UGameEngine.hpp"
"shared/sdk/UGameViewportClient.hpp"
"shared/sdk/UObjectArray.hpp"
"shared/sdk/UObjectBase.hpp"
"shared/sdk/Utility.hpp"
"shared/sdk/threading/GameThreadWorker.hpp"
"shared/sdk/threading/RHIThreadWorker.hpp"
Expand Down Expand Up @@ -414,6 +416,7 @@ list(APPEND sdk-nolog_SOURCES
"shared/sdk/UGameEngine.cpp"
"shared/sdk/UGameViewportClient.cpp"
"shared/sdk/UObjectArray.cpp"
"shared/sdk/UObjectBase.cpp"
"shared/sdk/Utility.cpp"
"shared/sdk/CVar.hpp"
"shared/sdk/ConsoleManager.hpp"
Expand All @@ -433,6 +436,7 @@ list(APPEND sdk-nolog_SOURCES
"shared/sdk/UGameEngine.hpp"
"shared/sdk/UGameViewportClient.hpp"
"shared/sdk/UObjectArray.hpp"
"shared/sdk/UObjectBase.hpp"
"shared/sdk/Utility.hpp"
"shared/sdk/threading/GameThreadWorker.hpp"
"shared/sdk/threading/RHIThreadWorker.hpp"
Expand Down
13 changes: 13 additions & 0 deletions shared/sdk/UClass.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include "UObject.hpp"

namespace sdk {
class UStruct : public UObject {
public:
};

class UClass : public UStruct {
public:
};
}
9 changes: 9 additions & 0 deletions shared/sdk/UObject.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include "UObjectBase.hpp"

namespace sdk {
class UObject : public UObjectBase {
public:
};
}
17 changes: 13 additions & 4 deletions shared/sdk/UObjectArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ FUObjectArray* FUObjectArray::get() {
return utility::ExhaustionResult::CONTINUE;
}

SPDLOG_INFO("Found GUObjectArray at 0x{:x}", *displacement);
SPDLOG_INFO("[FUObjectArray::get] Found GUObjectArray at 0x{:x}", *displacement);
result = potential_result;
return utility::ExhaustionResult::BREAK;
});
Expand All @@ -278,21 +278,30 @@ FUObjectArray* FUObjectArray::get() {
// do an initial first pass as a test
SPDLOG_INFO("[FUObjectArray::get] {} objects", result->get_object_count());

try {
if (auto item = result->get_object(0); item != nullptr) {
if (item->object != nullptr) {
item->object->update_offsets();
}
}
} catch(...) {
SPDLOG_ERROR("[FUObjectArray::get] Failed to update offsets");
}

for (auto i = 0; i < result->get_object_count(); ++i) {
auto item = result->get_object(i);
if (item == nullptr) {
continue;
}

auto obj = *(void**)item;
auto obj = *(sdk::UObjectBase**)item;

if (obj == nullptr || IsBadReadPtr(obj, sizeof(void*))) {
continue;
}

FName* fname = (FName*)((uintptr_t)obj + 0x18);
try {
const auto name = fname->to_string();
const auto name = obj->get_full_name();

SPDLOG_INFO("{} {}", i, utility::narrow(name));
} catch(...) {
Expand Down
3 changes: 1 addition & 2 deletions shared/sdk/UObjectArray.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#pragma once

#include <cstdint>
#include "UObjectBase.hpp"

namespace sdk {
class UObjectBase;

struct FUObjectItem {
UObjectBase* object{nullptr};
int32_t flags{0};
Expand Down
62 changes: 62 additions & 0 deletions shared/sdk/UObjectBase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include <Windows.h>
#include <spdlog/spdlog.h>

#include "UClass.hpp"
#include "UObjectBase.hpp"

namespace sdk {
void UObjectBase::update_offsets() {
if (s_attempted_update_offsets) {
return;
}

s_attempted_update_offsets = true;

SPDLOG_INFO("[UObjectBase] Bruteforcing offsets...");

// Look for the first valid pointer in the object array.
// The first valid pointer is ClassPrivate.
for (auto i = sizeof(void*); i < 0x50; i += sizeof(void*)) {
const auto value = *(void**)((uintptr_t)this + i);

if (value == nullptr || IsBadReadPtr(value, sizeof(void*)) || ((uintptr_t)value & 1) != 0) {
continue;
}

const auto possible_vtable = *(void**)value;

if (possible_vtable == nullptr || IsBadReadPtr(possible_vtable, sizeof(void*)) || ((uintptr_t)possible_vtable & 1) != 0) {
continue;
}

const auto possible_vfunc = *(void**)possible_vtable;

if (possible_vfunc == nullptr || IsBadReadPtr(possible_vfunc, sizeof(void*))) {
continue;
}

SPDLOG_INFO("[UObjectBase] Found ClassPrivate at offset 0x{:X}", i);
s_class_private_offset = i;
s_fname_offset = s_class_private_offset + sizeof(void*);
s_outer_private_offset = s_fname_offset + sizeof(void*);

break;
}
}

std::wstring UObjectBase::get_full_name() const {
auto c = get_class();

if (c == nullptr) {
return L"null";
}

auto obj_name = get_fname().to_string();

for (auto outer = this->get_outer(); outer != nullptr && outer != this; outer = outer->get_outer()) {
obj_name = outer->get_fname().to_string() + L'.' + obj_name;
}

return c->get_fname().to_string() + L' ' + obj_name;
}
}
42 changes: 42 additions & 0 deletions shared/sdk/UObjectBase.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include "FName.hpp"

namespace sdk {
class UClass;
class UObject;

class UObjectBase {
public:
void update_offsets();
std::wstring get_full_name() const;

UClass* get_class() const {
return *(UClass**)((uintptr_t)this + s_class_private_offset);
}

UObject* get_outer() const {
return *(UObject**)((uintptr_t)this + s_outer_private_offset);
}

FName& get_fname() const {
return *(FName*)((uintptr_t)this + s_fname_offset);
}

uint32_t get_object_flags() const {
return *(uint32_t*)((uintptr_t)this + s_object_flags_offset);
}

uint32_t get_internal_index() const {
return *(uint32_t*)((uintptr_t)this + s_internal_index_offset);
}

private:
static inline bool s_attempted_update_offsets{false};
static inline uint32_t s_object_flags_offset{0x8};
static inline uint32_t s_internal_index_offset{0xC};
static inline uint32_t s_class_private_offset{0x10};
static inline uint32_t s_fname_offset{0x18};
static inline uint32_t s_outer_private_offset{0x20};
};
}

0 comments on commit 5d4af5f

Please sign in to comment.