From 5c65ef9b7b67df30e854fef05e3c47086898a000 Mon Sep 17 00:00:00 2001 From: praydog Date: Sat, 28 Oct 2023 13:33:56 -0700 Subject: [PATCH] SDK: Bruteforce FArray/FObjectProperty offsets --- CMakeLists.txt | 4 ++ shared/sdk/FArrayProperty.cpp | 67 ++++++++++++++++++++++++++++++++++ shared/sdk/FArrayProperty.hpp | 18 +++++++++ shared/sdk/FObjectProperty.cpp | 53 +++++++++++++++++++++++++++ shared/sdk/FObjectProperty.hpp | 20 ++++++++++ shared/sdk/UObjectArray.cpp | 4 ++ 6 files changed, 166 insertions(+) create mode 100644 shared/sdk/FArrayProperty.cpp create mode 100644 shared/sdk/FArrayProperty.hpp create mode 100644 shared/sdk/FObjectProperty.cpp create mode 100644 shared/sdk/FObjectProperty.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1654721f..10ca7e23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -322,10 +322,12 @@ list(APPEND sdk_SOURCES "shared/sdk/ConsoleManager.cpp" "shared/sdk/DynamicRHI.cpp" "shared/sdk/EngineModule.cpp" + "shared/sdk/FArrayProperty.cpp" "shared/sdk/FBoolProperty.cpp" "shared/sdk/FField.cpp" "shared/sdk/FMalloc.cpp" "shared/sdk/FName.cpp" + "shared/sdk/FObjectProperty.cpp" "shared/sdk/FProperty.cpp" "shared/sdk/FRenderTargetPool.cpp" "shared/sdk/FSceneView.cpp" @@ -360,11 +362,13 @@ list(APPEND sdk_SOURCES "shared/sdk/ConsoleManager.hpp" "shared/sdk/DynamicRHI.hpp" "shared/sdk/EngineModule.hpp" + "shared/sdk/FArrayProperty.hpp" "shared/sdk/FBoolProperty.hpp" "shared/sdk/FField.hpp" "shared/sdk/FFieldClass.hpp" "shared/sdk/FMalloc.hpp" "shared/sdk/FName.hpp" + "shared/sdk/FObjectProperty.hpp" "shared/sdk/FProperty.hpp" "shared/sdk/FRenderTargetPool.hpp" "shared/sdk/FSceneView.hpp" diff --git a/shared/sdk/FArrayProperty.cpp b/shared/sdk/FArrayProperty.cpp new file mode 100644 index 00000000..1ef41f63 --- /dev/null +++ b/shared/sdk/FArrayProperty.cpp @@ -0,0 +1,67 @@ +#include +#include + +#include "UClass.hpp" +#include "UObjectArray.hpp" +#include "FObjectProperty.hpp" + +#include "FArrayProperty.hpp" + +namespace sdk { +void FArrayProperty::update_offsets() { + if (s_offsets_updated) { + return; + } + + s_offsets_updated = true; + + SPDLOG_INFO("[FArrayProperty::update_offsets] Updating offsets"); + + const auto scene_component = sdk::find_uobject(L"Class /Script/Engine.SceneComponent"); + + if (scene_component == nullptr) { + SPDLOG_ERROR("[FArrayProperty::update_offsets] Failed to find SceneComponent"); + return; + } + + const auto attach_children = scene_component->find_property(L"AttachChildren"); + + if (attach_children == nullptr) { + SPDLOG_ERROR("[FArrayProperty::update_offsets] Failed to find AttachChildren"); + return; + } + + // Start from FProperty's offset offset, and bruteforce until we find the correct offset + const auto initial_start = FProperty::s_offset_offset + 4 + sizeof(void*) + sizeof(void*); + // align up to sizeof(void*) + const auto start = (initial_start + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + + for (auto i = start; i < start + 0x100; i += sizeof(void*)) try { + const auto value = *(FProperty**)(attach_children + i); + + if (value == nullptr || IsBadReadPtr(value, sizeof(void*))) { + continue; + } + + const auto c = value->get_class(); + + if (c == nullptr || IsBadReadPtr(c, sizeof(void*))) { + continue; + } + + if (c->get_name().to_string() == L"ObjectProperty") { + auto prop = (FObjectProperty*)value; + + if (prop->get_property_class() == scene_component) { + s_inner_offset = i; + SPDLOG_INFO("[FArrayProperty::update_offsets] Found property inner offset: 0x{:X}", i); + break; + } + } + } catch(...) { + continue; + } + + SPDLOG_INFO("[FArrayProperty::update_offsets] Done"); +} +} \ No newline at end of file diff --git a/shared/sdk/FArrayProperty.hpp b/shared/sdk/FArrayProperty.hpp new file mode 100644 index 00000000..79c76715 --- /dev/null +++ b/shared/sdk/FArrayProperty.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "FProperty.hpp" + +namespace sdk { +class FArrayProperty : public FProperty { +public: + static void update_offsets(); + + FProperty* get_inner() const { + return *(FProperty**)((uintptr_t)this + s_inner_offset); + } + +private: + static inline bool s_offsets_updated{false}; + static inline uint32_t s_inner_offset{0x0}; +}; +} \ No newline at end of file diff --git a/shared/sdk/FObjectProperty.cpp b/shared/sdk/FObjectProperty.cpp new file mode 100644 index 00000000..c8a6f282 --- /dev/null +++ b/shared/sdk/FObjectProperty.cpp @@ -0,0 +1,53 @@ +#include +#include + +#include "UClass.hpp" +#include "UObjectArray.hpp" + +#include "FObjectProperty.hpp" + +namespace sdk { +void FObjectProperty::update_offsets() { + if (s_offsets_updated) { + return; + } + + s_offsets_updated = true; + + SPDLOG_INFO("[FObjectProperty::update_offsets] Updating offsets"); + + const auto scene_component = sdk::find_uobject(L"Class /Script/Engine.SceneComponent"); + + if (scene_component == nullptr) { + SPDLOG_ERROR("[FObjectProperty::update_offsets] Failed to find SceneComponent"); + return; + } + + const auto attach_parent = scene_component->find_property(L"AttachParent"); + + if (attach_parent == nullptr) { + SPDLOG_ERROR("[FObjectProperty::update_offsets] Failed to find AttachParent"); + return; + } + + // Start from FProperty's offset offset, and bruteforce until we find the correct offset + const auto initial_start = FProperty::s_offset_offset + 4 + sizeof(void*) + sizeof(void*); + // align up to sizeof(void*) + const auto start = (initial_start + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + + for (auto i = start; i < start + 0x100; i += sizeof(void*)) try { + const auto value = *(UClass**)(attach_parent + i); + + // well this is easy! + if (value == scene_component) { + s_property_class_offset = i; + SPDLOG_INFO("[FObjectProperty::update_offsets] Found property class offset: 0x{:X}", i); + break; + } + } catch(...) { + continue; + } + + SPDLOG_INFO("[FObjectProperty::update_offsets] Done"); +} +} \ No newline at end of file diff --git a/shared/sdk/FObjectProperty.hpp b/shared/sdk/FObjectProperty.hpp new file mode 100644 index 00000000..68999a26 --- /dev/null +++ b/shared/sdk/FObjectProperty.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "FProperty.hpp" + +namespace sdk { +class UClass; + +class FObjectProperty : public FProperty { +public: + static void update_offsets(); + + UClass* get_property_class() const { + return *(UClass**)((uintptr_t)this + s_property_class_offset); + } + +private: + static inline bool s_offsets_updated{false}; + static inline uint32_t s_property_class_offset{0x0}; +}; +} \ No newline at end of file diff --git a/shared/sdk/UObjectArray.cpp b/shared/sdk/UObjectArray.cpp index 5ec5fb19..d8b6acd9 100644 --- a/shared/sdk/UObjectArray.cpp +++ b/shared/sdk/UObjectArray.cpp @@ -20,6 +20,8 @@ #include "FField.hpp" #include "FStructProperty.hpp" #include "FBoolProperty.hpp" +#include "FObjectProperty.hpp" +#include "FArrayProperty.hpp" #include "UObjectArray.hpp" namespace sdk { @@ -389,6 +391,8 @@ FUObjectArray* FUObjectArray::get() try { sdk::UProperty::update_offsets(); sdk::FStructProperty::update_offsets(); sdk::FBoolProperty::update_offsets(); + sdk::FObjectProperty::update_offsets(); + sdk::FArrayProperty::update_offsets(); #ifdef TESTING_GUOBJECTARRAY try {