Skip to content

Commit

Permalink
GUObjectArray try catch blocks to prevent premature crashes
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Aug 22, 2023
1 parent 02663ab commit d00473f
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 33 deletions.
66 changes: 35 additions & 31 deletions shared/sdk/FName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,47 +349,51 @@ std::optional<FName::ToStringFn> standard_find_to_string() {
ctx.ctx->Registers.RegRip = (uintptr_t)*result;
ctx.ctx->Registers.RegRcx = 0x12345678; // magic number

utility::emulate(module, (uintptr_t)*result, 100, ctx, [&](const utility::ShemuContextExtended& ctx) -> utility::ExhaustionResult {
if (ctx.next.writes_to_memory || std::string_view{ctx.next.ix.Mnemonic}.starts_with("CALL")) {
return utility::ExhaustionResult::STEP_OVER;
}

const auto& nix = ctx.next.ix;
try {
utility::emulate(module, (uintptr_t)*result, 100, ctx, [&](const utility::ShemuContextExtended& ctx) -> utility::ExhaustionResult {
if (ctx.next.writes_to_memory || std::string_view{ctx.next.ix.Mnemonic}.starts_with("CALL")) {
return utility::ExhaustionResult::STEP_OVER;
}

const auto& nix = ctx.next.ix;

if (nix.OperandsCount < 2) {
return utility::ExhaustionResult::CONTINUE;
}
if (nix.OperandsCount < 2) {
return utility::ExhaustionResult::CONTINUE;
}

if (nix.Operands[1].Type == ND_OP_MEM && nix.Operands[1].Info.Memory.HasBase &&
nix.Operands[1].Info.Memory.HasDisp && nix.Operands[1].Info.Memory.Disp >= 8)
{
const auto reg_value = ((ND_UINT64*)&ctx.ctx->ctx->Registers)[nix.Operands[1].Info.Memory.Base];
if (nix.Operands[1].Type == ND_OP_MEM && nix.Operands[1].Info.Memory.HasBase &&
nix.Operands[1].Info.Memory.HasDisp && nix.Operands[1].Info.Memory.Disp >= 8)
{
const auto reg_value = ((ND_UINT64*)&ctx.ctx->ctx->Registers)[nix.Operands[1].Info.Memory.Base];

if (reg_value == 0x12345678) {
char text[ND_MIN_BUF_SIZE];
NdToText(&nix, 0, sizeof(text), text);
SPDLOG_INFO("{}", text);
SPDLOG_INFO("FName::get_to_string: Found function is not inlined (UClass::GetName)");
if (reg_value == 0x12345678) {
char text[ND_MIN_BUF_SIZE];
NdToText(&nix, 0, sizeof(text), text);
SPDLOG_INFO("{}", text);
SPDLOG_INFO("FName::get_to_string: Found function is not inlined (UClass::GetName)");

const auto next_call = utility::scan_mnemonic(ctx.ctx->ctx->Registers.RegRip, 20, "CALL");
const auto next_call = utility::scan_mnemonic(ctx.ctx->ctx->Registers.RegRip, 20, "CALL");

if (next_call) {
const auto resolved_call = utility::resolve_displacement(*next_call);
if (next_call) {
const auto resolved_call = utility::resolve_displacement(*next_call);

if (resolved_call) {
*result = (FName::ToStringFn)utility::calculate_absolute(*next_call + 1);
SPDLOG_INFO("FName::get_to_string: Found function to use instead {:x}", (uintptr_t)*result);
} else {
SPDLOG_ERROR("FName::get_to_string: Failed to resolve displacement for next call");
if (resolved_call) {
*result = (FName::ToStringFn)utility::calculate_absolute(*next_call + 1);
SPDLOG_INFO("FName::get_to_string: Found function to use instead {:x}", (uintptr_t)*result);
} else {
SPDLOG_ERROR("FName::get_to_string: Failed to resolve displacement for next call");
}
}
}

return utility::ExhaustionResult::BREAK;
return utility::ExhaustionResult::BREAK;
}
}
}

return utility::ExhaustionResult::CONTINUE;
});
return utility::ExhaustionResult::CONTINUE;
});
} catch(...) {
SPDLOG_ERROR("FName::get_to_string: Failed to emulate");
}

SPDLOG_INFO("FName::get_to_string: result={:x}", (uintptr_t)*result);

Expand Down
102 changes: 100 additions & 2 deletions shared/sdk/UObjectArray.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
#include <unordered_map>
#include <unordered_set>
#include <shared_mutex>

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

#include "EngineModule.hpp"
#include "UEngine.hpp"
#include "UGameplayStatics.hpp"
#include "APlayerController.hpp"

#include "UObject.hpp"
#include "UClass.hpp"
#include "UProperty.hpp"
#include "FName.hpp"
#include "FField.hpp"
#include "UObjectArray.hpp"

namespace sdk {
Expand Down Expand Up @@ -55,7 +60,7 @@ UObjectBase* find_uobject(const std::wstring& full_name, bool cached) {
return nullptr;
}

FUObjectArray* FUObjectArray::get() {
FUObjectArray* FUObjectArray::get() try {
static auto result = []() -> FUObjectArray* {
SPDLOG_INFO("[FUObjectArray::get] Searching for FUObjectArray...");

Expand Down Expand Up @@ -356,7 +361,90 @@ FUObjectArray* FUObjectArray::get() {
sdk::UScriptStruct::update_offsets();
sdk::UProperty::update_offsets();

for (auto i = 0; i < result->get_object_count(); ++i) {
try {
const auto world = sdk::UEngine::get()->get_world();

SPDLOG_INFO("[FUObjectArray::get] World: 0x{:x}", (uintptr_t)world);

// Testing caching
const auto world2 = sdk::UEngine::get()->get_world();

SPDLOG_INFO("[FUObjectArray::get] World2: 0x{:x}", (uintptr_t)world2);

const auto world_name = world->get_full_name();
SPDLOG_INFO("[FUObjectArray::get] World name: {}", utility::narrow(world_name));

const auto player_controller = sdk::UGameplayStatics::get()->get_player_controller(world, 0);
SPDLOG_INFO("[FUObjectArray::get] PlayerController: 0x{:x}", (uintptr_t)player_controller);

const auto pawn = sdk::UEngine::get()->get_localpawn(0);
SPDLOG_INFO("[FUObjectArray::get] Pawn: 0x{:x}", (uintptr_t)pawn);

if (player_controller != nullptr) {
SPDLOG_INFO("[FUObjectArray::get] Pawn2: 0x{:x}", (uintptr_t)player_controller->get_acknowledged_pawn());
}
} catch(...) {
SPDLOG_ERROR("[FUObjectArray::get] Unknown exception occurred while performing tests on GUObjectArray");
}

std::unordered_set<std::wstring> possible_field_types{};

const auto class_t = sdk::UClass::static_class();

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

const auto obj = (sdk::UObject*)item->object;

if (obj == nullptr) {
continue;
}

if (!obj->is_a(class_t)) {
continue;
}

const auto c = (sdk::UClass*)obj;

for (auto f = c->get_child_properties(); f != nullptr; f = f->get_next()) {
// TODO: the other one
if (FField::is_ufield_only()) {
const auto ufield = (sdk::UField*)f;
const auto f_class = ufield->get_class();

if (f_class == nullptr) {
continue;
}

const auto f_class_name = f_class->get_full_name();

if (!possible_field_types.contains(f_class_name)) {
SPDLOG_INFO("[FUObjectArray::get] Possible field type: {}", utility::narrow(f_class_name));
possible_field_types.insert(f_class_name);
}
} else {
const auto f_class = f->get_class();

if (f_class == nullptr) {
continue;
}

const auto f_class_name = f_class->get_name().to_string();

if (!possible_field_types.contains(f_class_name)) {
SPDLOG_INFO("[FUObjectArray::get] Possible field type: {}", utility::narrow(f_class_name));
possible_field_types.insert(f_class_name);
}
}
}
} catch(...) {
continue;
}

for (auto i = 0; i < result->get_object_count(); ++i) try {
auto item = result->get_object(i);
if (item == nullptr) {
continue;
Expand All @@ -375,9 +463,19 @@ FUObjectArray* FUObjectArray::get() {
} catch(...) {
SPDLOG_ERROR("Failed to get name {}", i);
}
} catch(...) {
SPDLOG_ERROR("[FUObjectArray::get] Exception: failed to get object {}", i);
}
};

return result;
} catch(...) {
static bool once = true;
if (once) {
SPDLOG_ERROR("[FUObjectArray::get] Failed to get GUObjectArray, prevented a crash");
once = false;
}

return nullptr;
}
}

0 comments on commit d00473f

Please sign in to comment.