mirror of
https://github.com/ApfelTeeSaft/expresso.git
synced 2026-08-26 19:23:27 +00:00
175 lines
7.1 KiB
C++
175 lines
7.1 KiB
C++
// i'm so fucking tired dude
|
|
#include "expresso_recovered.h"
|
|
|
|
#include <cstring>
|
|
|
|
namespace expresso {
|
|
|
|
// SERVER_ funcs used by this POS
|
|
const char* const kRecoveredServerRpcs[] = {
|
|
"SERVER_OnBlock", // 0x59A3D8
|
|
"SERVER_Attack", // 0x59A3E8
|
|
"SERVER_Dodge", // 0x59A3F8
|
|
"SERVER_SetCombatStance", // 0x59A408
|
|
"SERVER_FillAbilities", // 0x59A420
|
|
"SERVER_ActivateRage", // 0x59A440
|
|
"SERVER_SetShift", // 0x59A458
|
|
"SERVER_SetCharacterDisabled", // 0x59A480
|
|
"SERVER_LockFearTo", // 0x59A4A0
|
|
"SERVER_SetSprintingWithButtonState", // 0x59A4B8
|
|
"SERVER_DeadFriendlySpotted", // 0x59A4E0
|
|
"SERVER_UseCurrentSmallItem", // 0x59A568
|
|
"SERVER_UseCurrentLargeItem", // 0x59A588
|
|
"SERVER_DestroyDoor", // 0x59A660
|
|
"SERVER_DeactivateLights", // 0x59A6A8
|
|
"SERVER_NotifyMinigameFail", // 0x59A748
|
|
"SERVER_NotifyKillerOfMinigameFail", // 0x59A768
|
|
"SERVER_OpenDoor", // 0x59A798
|
|
"SERVER_ForceStopInteractAnim", // 0x59A7A8
|
|
"SERVER_FinishInteractAnim", // 0x59A7C8
|
|
"SERVER_CancelInteractAnim", // 0x59A7E8
|
|
"SERVER_OnMorph", // 0x59B3C0
|
|
};
|
|
const size_t kRecoveredServerRpcCount =
|
|
sizeof(kRecoveredServerRpcs) / sizeof(kRecoveredServerRpcs[0]);
|
|
|
|
// RVA 0x160B80 shape every SERVER_* call site shares
|
|
//
|
|
// 0x160BB5 cmp qword [g_ProcessEvent], 0 ; resolve once
|
|
// 0x160BBF lea rax, [rbx + 0x7A8010] ; gameBase + kProcessEvent
|
|
// 0x160BD4 lea r8, "SERVER_SetShift"
|
|
// 0x160BE1 call FindFunctionByName
|
|
// 0x160BE6 <canonical check on the result>
|
|
// 0x160BF7 mov r9d, 0x40400 ; FUNC_Native | FUNC_Private
|
|
// 0x160BFD mov byte [rsp+0x20], 1 ; params.bEnable = true
|
|
// 0x160C0D call CallUFunction
|
|
bool InvokeServerRpc(IMemory& mem, uint64_t game_base, uint64_t target_object,
|
|
const ServerRpcCall& call,
|
|
ProcessEventFn invoke, void* user) {
|
|
if (call.function_name == nullptr) return false;
|
|
if (!IsCanonicalUserPointer(target_object)) return false;
|
|
|
|
const uint64_t fn =
|
|
FindFunctionByName(mem, game_base, target_object, call.function_name);
|
|
if (!IsCanonicalUserPointer(fn)) return false; // 0x160BF0 / 0x160BF5
|
|
|
|
unsigned char frame[256] = {0};
|
|
const size_t n = (call.params_size < sizeof(frame)) ? call.params_size
|
|
: sizeof(frame);
|
|
if (call.params != nullptr && n != 0) std::memcpy(frame, call.params, n);
|
|
|
|
return CallUFunction(mem, target_object, fn, frame, call.or_flags,
|
|
invoke, user);
|
|
}
|
|
|
|
// RVA 0x16606C .. 0x16609D -- the "[DW:*]" direct write
|
|
//
|
|
// 0x166065 lea r14, [rax + 0x370] ; &PlayerState->PlayerName (FString)
|
|
// 0x16606C lea rdx, [g_wideNameBuf]
|
|
// 0x166076 call SafeWritePtr ; FString.Data = buffer
|
|
// 0x16607E lea rcx, [r14 + 8]
|
|
// 0x166088 call SafeWriteInt32 ; FString.ArrayNum = len
|
|
// 0x166093 lea rcx, [r14 + 0xC]
|
|
// 0x16609D call SafeWriteInt32 ; FString.ArrayMax = len
|
|
//
|
|
// No RPC, no engine call, three stores into a replicated property. Any
|
|
// server-side name policy that reads the replicated value is defeated by this;
|
|
// the name must be owned by the server (ROSE JOHN: please fix this or else)
|
|
bool DirectWritePlayerName(IMemory& mem, uint64_t player_state,
|
|
uint64_t wide_buffer, int32_t length) {
|
|
if (!IsCanonicalUserPointer(player_state)) return false;
|
|
|
|
const uint64_t fstring = player_state + world::kPlayerNameFString;
|
|
|
|
const bool ok_data = SafeWritePtr (mem, fstring + 0x0, wide_buffer);
|
|
const bool ok_num = SafeWriteInt32(mem, fstring + 0x8, length);
|
|
const bool ok_max = SafeWriteInt32(mem, fstring + 0xC, length);
|
|
|
|
return ok_data && ok_num && ok_max; // 0x1660A5 AND chain
|
|
}
|
|
|
|
// RVA 0x165D90, the rename driver, in the order the recovered code tries
|
|
//
|
|
// Status strings, all in restored .rdata:
|
|
// "[SCN:noFn]" 0x59A858 "[SCN:OK]" 0x59A868 "[SCN:EX]" 0x59A878
|
|
// "[BP:noLib]" 0x59A888 "[BP:noFn]" 0x59A8A8 "[BP:OK]" 0x59A8B8
|
|
// "[DW:noPS]" 0x59A8C8 "[DW:OK]" 0x59A8D8 "[DW:FAIL]" 0x59A8E0
|
|
// "ERR: empty" 0x59A8F8 "ERR: too long" 0x59A908
|
|
// "COOLDOWN %llums" 0x59A918 "ERR: PE null" 0x59A928
|
|
RenameResult SpoofPlayerName(IMemory& mem, uint64_t game_base,
|
|
const char* utf8_name, uint64_t now_ms,
|
|
uint64_t& last_rename_ms,
|
|
uint64_t wide_buffer, int32_t& wide_length,
|
|
ProcessEventFn invoke, void* user) {
|
|
if (utf8_name == nullptr || utf8_name[0] == '\0') // 0x165E1E
|
|
return RenameResult::kEmpty;
|
|
|
|
const size_t len = std::strlen(utf8_name);
|
|
if (len > limits::kMaxNameChars) // 0x165E2A cmp rax,0x78
|
|
return RenameResult::kTooLong;
|
|
|
|
// 0x165E5A: 3000 ms client-side cooldown, reported as "COOLDOWN %llums"
|
|
if (last_rename_ms != 0 &&
|
|
now_ms - last_rename_ms < limits::kRenameCooldown) {
|
|
return RenameResult::kCooldown;
|
|
}
|
|
last_rename_ms = now_ms; // 0x165EA5 xchg
|
|
|
|
// 0x165EBF: ProcessEvent is resolved lazily, from a fixed RVA
|
|
const uint64_t process_event = game_base + game_rva::kProcessEvent;
|
|
if (!IsCanonicalUserPointer(process_event)) // 0x165ED9
|
|
return RenameResult::kNoProcessEvent;
|
|
|
|
LocalPlayerCache cache;
|
|
const uint64_t pc =
|
|
GetLocalPlayerController(mem, game_base, cache, now_ms); // 0x165EEE
|
|
if (!IsCanonicalUserPointer(pc)) // 0x165F0A
|
|
return RenameResult::kNoPlayerController;
|
|
|
|
wide_length = static_cast<int32_t>(len);
|
|
|
|
// ServerChangeName RPC (RVA 0x165B60, flags 0x400)
|
|
// CFG does not even have this function, is this guy (or his AI Agent) retarded?
|
|
{
|
|
struct FStringParam { // 0x165BB5..0x165BCB
|
|
uint64_t data;
|
|
int32_t array_num;
|
|
int32_t array_max;
|
|
} params{wide_buffer, wide_length, wide_length};
|
|
|
|
const ServerRpcCall call{"ServerChangeName", func_flags::kNative,
|
|
¶ms, sizeof(params)};
|
|
if (InvokeServerRpc(mem, game_base, pc, call, invoke, user))
|
|
return RenameResult::kOk; // "[SCN:OK]"
|
|
}
|
|
|
|
// AdvancedSessionsLibrary CDO SetPlayerName (0x165C50)
|
|
// Resolved as FindFunctionByName(CDO("Default__AdvancedSessionsLibrary"),
|
|
// "SetPlayerName").
|
|
{
|
|
struct SetPlayerNameParams {
|
|
uint64_t player_controller;
|
|
uint64_t name_data;
|
|
int32_t name_num;
|
|
int32_t name_max;
|
|
} params{pc, wide_buffer, wide_length, wide_length};
|
|
|
|
const ServerRpcCall call{"SetPlayerName", func_flags::kNative,
|
|
¶ms, sizeof(params)};
|
|
if (InvokeServerRpc(mem, game_base, pc, call, invoke, user))
|
|
return RenameResult::kOk; // "[BP:OK]"
|
|
}
|
|
|
|
// direct FString write (0x166036)
|
|
// ich ficke dieses networking frfr
|
|
const uint64_t player_state = SafeReadPtr(mem, pc + world::kPlayerState);
|
|
if (!IsCanonicalUserPointer(player_state))
|
|
return RenameResult::kFailed; // "[DW:noPS]"
|
|
|
|
return DirectWritePlayerName(mem, player_state, wide_buffer, wide_length)
|
|
? RenameResult::kOk // "[DW:OK]"
|
|
: RenameResult::kFailed; // "[DW:FAIL]"
|
|
}
|
|
|
|
} // namespace expresso
|