// i'm so fucking tired dude #include "expresso_recovered.h" #include 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 // 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(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