Skip to content

Commit

Permalink
SDK: Bruteforce UObject::ProcessEvent index
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Jul 9, 2023
1 parent 90caea0 commit 059a13d
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 1 deletion.
2 changes: 2 additions & 0 deletions shared/sdk/UClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,8 @@ void UStruct::resolve_function_offsets(uint32_t child_search_start) try {
} else {
SPDLOG_ERROR("[UStruct] Failed to find native function offset!");
}

UObjectBase::update_process_event_index();
} catch(...) {
SPDLOG_ERROR("[UStruct::resolve_function_offsets] Failed to resolve function offsets! (unknown exception)");
}
Expand Down
2 changes: 1 addition & 1 deletion shared/sdk/UObjectArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ FUObjectArray* FUObjectArray::get() {
}

// Attempt to find the SuperStruct offset
sdk::UStruct::update_offsets();
sdk::UClass::update_offsets();
sdk::UStruct::update_offsets();
sdk::UScriptStruct::update_offsets();
sdk::UProperty::update_offsets();

Expand Down
131 changes: 131 additions & 0 deletions shared/sdk/UObjectBase.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#include <unordered_set>

#include <Windows.h>
#include <spdlog/spdlog.h>
#include <utility/Scan.hpp>

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

namespace sdk {
Expand Down Expand Up @@ -44,6 +48,133 @@ void UObjectBase::update_offsets() {
}
}

void UObjectBase::update_process_event_index() try {
const auto object_class = (sdk::UClass*)sdk::find_uobject(L"Class /Script/CoreUObject.Object");

if (object_class == nullptr) {
SPDLOG_ERROR("[UObjectBase] Failed to find UObject class, cannot update ProcessEvent index");
return;
}

const auto default_object_class = object_class->get_class_default_object();

if (default_object_class == nullptr) {
SPDLOG_ERROR("[UObjectBase] Failed to find default UObject, cannot update ProcessEvent index");
return;
}

const auto vtable = *(void***)default_object_class;

if (vtable == nullptr || IsBadReadPtr(vtable, sizeof(void*))) {
SPDLOG_ERROR("[UObjectBase] Failed to find UObject vtable, cannot update ProcessEvent index");
return;
}

// Walk the vtable, looking for the ProcessEvent function
std::unordered_set<uintptr_t> seen_ips{};

for (auto i = 0; i < 100; ++i) {
const auto vfunc = vtable[i];

if (vfunc == nullptr || IsBadReadPtr(vfunc, sizeof(void*))) {
break; // reached the end of the vtable
}

// Disassemble the func now, looking for displacements
// treat the displacement as a separate vtable
// we will walk that other vtable's functions, and look for string references to "Script Call Stack:\n"
// this is a dead giveaway that we have found the ProcessEvent function
// there's also another string - but we won't worry about that one for now
bool found = false;

const wchar_t target_string[] = L"Script call stack:\n";
const size_t target_length = sizeof(target_string) - sizeof(wchar_t); // Do not count the null terminator

utility::exhaustive_decode((uint8_t*)vfunc, 1000, [&](INSTRUX& ix, uintptr_t ip) -> utility::ExhaustionResult {
try {
if (found) {
return utility::ExhaustionResult::BREAK;
}

if (seen_ips.find(ip) != seen_ips.end()) {
return utility::ExhaustionResult::BREAK;
}

seen_ips.insert(ip);

// No need to deal with this one below. We will still decode both branches.
if (ix.BranchInfo.IsBranch) {
return utility::ExhaustionResult::CONTINUE;
}

const auto displacement = utility::resolve_displacement(ip);

if (!displacement) {
return utility::ExhaustionResult::CONTINUE;
}

if (*displacement == 0x0 || IsBadReadPtr((void*)*displacement, sizeof(void*) * 4)) {
return utility::ExhaustionResult::CONTINUE;
}

const auto inner_vtable = (void**)*displacement;

for (auto j = 0; j < 10; ++j) {
const auto inner_vfunc = inner_vtable[j];

if (inner_vfunc == nullptr || IsBadReadPtr(inner_vfunc, sizeof(void*))) {
break;
}

utility::exhaustive_decode((uint8_t*)inner_vfunc, 1000, [&](INSTRUX& ix2, uintptr_t ip2) -> utility::ExhaustionResult {
try {
if (found) {
return utility::ExhaustionResult::BREAK;
}

if (seen_ips.find(ip2) != seen_ips.end()) {
return utility::ExhaustionResult::BREAK;
}

seen_ips.insert(ip2);

const auto displacement2 = utility::resolve_displacement(ip2);

if (!displacement2) {
return utility::ExhaustionResult::CONTINUE;
}

const auto potential_string = (wchar_t*)*displacement2;

if (IsBadReadPtr(potential_string, target_length)) {
return utility::ExhaustionResult::CONTINUE;
}

if (std::memcmp(potential_string, target_string, target_length) == 0) {
UObjectBase::s_process_event_index = i;
found = true;

SPDLOG_INFO("[UObjectBase] Found ProcessEvent index at {}", UObjectBase::s_process_event_index);
return utility::ExhaustionResult::BREAK;
}
} catch(...) {
// dont care
}

return utility::ExhaustionResult::CONTINUE;
});
}
} catch(...) {
// dont care
}

return utility::ExhaustionResult::CONTINUE;
});
}
} catch(...) {
SPDLOG_ERROR("[UObjectBase] Failed to update ProcessEvent index");
}

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

Expand Down
7 changes: 7 additions & 0 deletions shared/sdk/UObjectBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "FName.hpp"

namespace sdk {
class UStruct;
class UClass;
class UObject;

Expand Down Expand Up @@ -36,11 +37,17 @@ class UObjectBase {
}

private:
static void update_process_event_index();

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};

static inline uint32_t s_process_event_index{0};

friend class UStruct;
};
}

0 comments on commit 059a13d

Please sign in to comment.