From b9288ee879c5a5e3d41f0d8a9e7cc58f88a1a5f0 Mon Sep 17 00:00:00 2001 From: praydog Date: Thu, 6 Jul 2023 20:59:23 -0700 Subject: [PATCH] SDK: Bruteforce UScriptStruct::StructOps offset --- shared/sdk/UClass.cpp | 84 +++++++++++++++++++++++++++++++++++++ shared/sdk/UClass.hpp | 51 +++++++++++++++++++++- shared/sdk/UObject.cpp | 5 +++ shared/sdk/UObject.hpp | 7 ++++ shared/sdk/UObjectArray.cpp | 1 + 5 files changed, 146 insertions(+), 2 deletions(-) diff --git a/shared/sdk/UClass.cpp b/shared/sdk/UClass.cpp index 1fcd50f3..797a9c8d 100644 --- a/shared/sdk/UClass.cpp +++ b/shared/sdk/UClass.cpp @@ -18,6 +18,23 @@ UClass* UClass::static_class() { return (UClass*)sdk::find_uobject(L"Class /Script/CoreUObject.Class"); } +void UField::update_offsets() { + if (s_attempted_update_offsets) { + return; + } + + s_attempted_update_offsets = true; + + SPDLOG_INFO("[UField] Bruteforcing offsets..."); + + UStruct::update_offsets(); + + // best thing i can come up with for now. + s_next_offset = UStruct::s_super_struct_offset - sizeof(void*); + + SPDLOG_INFO("[UField] Found Next at offset 0x{:X}", s_next_offset); +} + void UStruct::update_offsets() { if (s_attempted_update_offsets) { return; @@ -55,6 +72,8 @@ void UStruct::update_offsets() { } else { SPDLOG_ERROR("[UStruct] Failed to find classes!"); } + + UField::update_offsets(); } void UClass::update_offsets() { @@ -105,4 +124,69 @@ void UClass::update_offsets() { continue; } } + +UClass* UScriptStruct::static_class() { + return (UClass*)sdk::find_uobject(L"Class /Script/CoreUObject.ScriptStruct"); +} + +void UScriptStruct::update_offsets() { + if (s_attempted_update_offsets) { + return; + } + + s_attempted_update_offsets = true; + + UStruct::update_offsets(); + + SPDLOG_INFO("[UScriptStruct] Bruteforcing offsets..."); + + const auto matrix_scriptstruct = sdk::find_uobject(L"ScriptStruct /Script/CoreUObject.Matrix"); + const auto vector_scriptstruct = sdk::find_uobject(L"ScriptStruct /Script/CoreUObject.Vector"); + + if (matrix_scriptstruct == nullptr || vector_scriptstruct == nullptr) { + SPDLOG_ERROR("[UScriptStruct] Failed to find Matrix/Vector!"); + return; + } + + constexpr auto MAT4_SIZE_FLOAT = 4 * 4 * sizeof(float); + constexpr auto MAT4_SIZE_DOUBLE = 4 * 4 * sizeof(double); + constexpr auto VECTOR_SIZE_FLOAT = 3 * sizeof(float); + constexpr auto VECTOR_SIZE_DOUBLE = 3 * sizeof(double); + + for (auto i = UStruct::s_super_struct_offset; i < 0x300; i += sizeof(void*)) try { + const auto value = *(sdk::UScriptStruct::StructOps**)((uintptr_t)matrix_scriptstruct + i); + + if (value == nullptr || IsBadReadPtr(value, sizeof(void*)) || ((uintptr_t)value & 1) != 0) { + continue; + } + + const auto potential_vtable = *(void**)value; + + if (potential_vtable == nullptr || IsBadReadPtr(potential_vtable, sizeof(void*)) || ((uintptr_t)potential_vtable & 1) != 0) { + continue; + } + + const auto potential_vfunc = *(void**)potential_vtable; + + if (potential_vfunc == nullptr || IsBadReadPtr(potential_vfunc, sizeof(void*))) { + continue; + } + + const auto value2 = *(sdk::UScriptStruct::StructOps**)((uintptr_t)vector_scriptstruct + i); + + if (value2 == nullptr || IsBadReadPtr(value2, sizeof(void*)) || ((uintptr_t)value2 & 1) != 0) { + continue; + } + + if ((value->size == MAT4_SIZE_FLOAT && value2->size == VECTOR_SIZE_FLOAT) || + (value->size == MAT4_SIZE_DOUBLE && value2->size == VECTOR_SIZE_DOUBLE)) + { + SPDLOG_INFO("[UScriptStruct] Found StructOps at offset 0x{:X}", i); + s_struct_ops_offset = i; + break; + } + } catch(...) { + continue; + } +} } \ No newline at end of file diff --git a/shared/sdk/UClass.hpp b/shared/sdk/UClass.hpp index 51086a70..fa62f09c 100644 --- a/shared/sdk/UClass.hpp +++ b/shared/sdk/UClass.hpp @@ -8,6 +8,11 @@ class UClass; class UField : public UObject { public: static UClass* static_class(); + static void update_offsets(); + +protected: + static inline bool s_attempted_update_offsets{false}; + static inline uint32_t s_next_offset{0x30}; // not correct always, we bruteforce it later }; class UStruct : public UField { @@ -19,9 +24,21 @@ class UStruct : public UField { return *(UStruct**)((uintptr_t)this + s_super_struct_offset); } -private: + bool is_a(UStruct* other) const { + for (auto super = this; super != nullptr; super = super->get_super_struct()) { + if (super == other) { + return true; + } + } + + return false; + } + +protected: static inline bool s_attempted_update_offsets{false}; static inline uint32_t s_super_struct_offset{0x40}; // not correct always, we bruteforce it later + + friend class UField; }; class UClass : public UStruct { @@ -33,8 +50,38 @@ class UClass : public UStruct { return *(UObject**)((uintptr_t)this + s_default_object_offset); } -private: +protected: static inline bool s_attempted_update_offsets{false}; static inline uint32_t s_default_object_offset{0x118}; // not correct always, we bruteforce it later }; + +class UScriptStruct : public UStruct { +public: + struct StructOps { + virtual ~StructOps() {}; + + int32_t size; + int32_t alignment; + }; + + static UClass* static_class(); + static void update_offsets(); + + StructOps* get_struct_ops() const { + return *(StructOps**)((uintptr_t)this + s_struct_ops_offset); + } + + int32_t get_struct_size() const { + const auto ops = get_struct_ops(); + if (ops == nullptr) { + return 0; + } + + return ops->size; + } + +protected: + static inline bool s_attempted_update_offsets{false}; + static inline uint32_t s_struct_ops_offset{0xB8}; // not correct always, we bruteforce it later +}; } \ No newline at end of file diff --git a/shared/sdk/UObject.cpp b/shared/sdk/UObject.cpp index 8b62e3f9..270780c0 100644 --- a/shared/sdk/UObject.cpp +++ b/shared/sdk/UObject.cpp @@ -1,9 +1,14 @@ #include "UObjectArray.hpp" +#include "UClass.hpp" #include "UObject.hpp" namespace sdk { UClass* UObject::static_class() { return (UClass*)sdk::find_uobject(L"Class /Script/CoreUObject.Object"); } + +bool UObject::is_a(UClass* other) const { + return get_class()->is_a((UStruct*)other); +} } \ No newline at end of file diff --git a/shared/sdk/UObject.hpp b/shared/sdk/UObject.hpp index 2c7454cc..d56a67b9 100644 --- a/shared/sdk/UObject.hpp +++ b/shared/sdk/UObject.hpp @@ -8,5 +8,12 @@ class UClass; class UObject : public UObjectBase { public: static UClass* static_class(); + + template + bool is_a() const { + return is_a(T::static_class()); + } + + bool is_a(UClass* cmp) const; }; } \ No newline at end of file diff --git a/shared/sdk/UObjectArray.cpp b/shared/sdk/UObjectArray.cpp index 4613ed12..9d87a976 100644 --- a/shared/sdk/UObjectArray.cpp +++ b/shared/sdk/UObjectArray.cpp @@ -350,6 +350,7 @@ FUObjectArray* FUObjectArray::get() { // Attempt to find the SuperStruct offset sdk::UStruct::update_offsets(); sdk::UClass::update_offsets(); + sdk::UScriptStruct::update_offsets(); for (auto i = 0; i < result->get_object_count(); ++i) { auto item = result->get_object(i);