From 44269fcf35f71e7e038ab4d70c1b3be841ec1e59 Mon Sep 17 00:00:00 2001 From: maybegreat48 <96936658+maybegreat48@users.noreply.github.com> Date: Mon, 13 May 2024 17:01:47 +0000 Subject: [PATCH] Desync protection improvements (#3094) --- src/backend/backend.cpp | 1 + src/backend/looped/looped.hpp | 1 + .../looped/session/chat_translator.cpp | 2 +- .../looped/session/modder_detection.cpp | 60 +++++++++++++++++++ src/core/data/infractions.hpp | 2 +- src/function_types.hpp | 2 + src/gta_pointers.hpp | 5 ++ src/hooking/hooking.cpp | 3 + src/hooking/hooking.hpp | 2 + .../misc/serialize_join_request_message.cpp | 1 + .../assign_physical_index.cpp | 10 ++++ .../protections/received_clone_create.cpp | 8 +++ src/hooks/protections/searchlight_crash.cpp | 13 ++++ src/pointers.cpp | 19 ++++++ 14 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/backend/looped/session/modder_detection.cpp create mode 100644 src/hooks/protections/searchlight_crash.cpp diff --git a/src/backend/backend.cpp b/src/backend/backend.cpp index 9646abdd..e173fa13 100644 --- a/src/backend/backend.cpp +++ b/src/backend/backend.cpp @@ -126,6 +126,7 @@ namespace big looped::session_auto_kick_host(); looped::session_block_jobs(); looped::session_chat_translator(); + looped::session_modder_detection(); if (g_script_connection_service) g_script_connection_service->on_tick(); diff --git a/src/backend/looped/looped.hpp b/src/backend/looped/looped.hpp index 0ddfbdd5..4c188402 100644 --- a/src/backend/looped/looped.hpp +++ b/src/backend/looped/looped.hpp @@ -36,6 +36,7 @@ namespace big static void session_randomize_ceo_colors(); static void session_auto_kick_host(); static void session_chat_translator(); + static void session_modder_detection(); static void system_self_globals(); static void system_update_pointers(); diff --git a/src/backend/looped/session/chat_translator.cpp b/src/backend/looped/session/chat_translator.cpp index a8bb97ff..2d821b9d 100644 --- a/src/backend/looped/session/chat_translator.cpp +++ b/src/backend/looped/session/chat_translator.cpp @@ -9,7 +9,7 @@ namespace big void looped::session_chat_translator() { - if (!translate_queue.empty() and !translate_lock and g.session.chat_translator.enabled) + if (!translate_queue.empty() && !translate_lock && g.session.chat_translator.enabled) { if (translate_queue.size() >= 3) { diff --git a/src/backend/looped/session/modder_detection.cpp b/src/backend/looped/session/modder_detection.cpp new file mode 100644 index 00000000..736c14e9 --- /dev/null +++ b/src/backend/looped/session/modder_detection.cpp @@ -0,0 +1,60 @@ +#include "backend/looped/looped.hpp" +#include "gta_util.hpp" +#include "natives.hpp" +#include "pointers.hpp" +#include "util/session.hpp" + +namespace big +{ + static bool bLastInSession = false; + + void looped::session_modder_detection() + { + if (!bLastInSession && *g_pointers->m_gta.m_is_session_started) + { + if (!g_player_service->get_self()->is_host()) + { + player_ptr host; + int players{}; // except you + int opens{}; + std::vector modded{}; + + for (auto& player : g_player_service->players()) + { + if (player.second->is_valid() && player.second->get_net_data()) + { + if (player.second->is_host()) + host = player.second; + + if (player.second->get_net_data()->m_nat_type == 0) + modded.push_back(player.second); + else if (player.second->get_net_data()->m_nat_type == 1) + opens++; + + players++; + } + } + + if (players > 5 && host && host->get_net_data()->m_nat_type != 0) // safe threshold + { + if ((modded.size() / (float)(players - 1)) < 0.5) // anything higher than this indicates that something fishy went on with the last host + { + for (auto& player : modded) + session::add_infraction(player, Infraction::DESYNC_PROTECTION); + } + + if (opens == (players - 1) && host->get_net_data()->m_nat_type > 1) // some dumb menus actually do this + { + session::add_infraction(host, Infraction::DESYNC_PROTECTION); // false positives are possible (like the moment a modder host leaves), but should be hopefully rare + } + } + } + + bLastInSession = true; + } + else if (bLastInSession && !*g_pointers->m_gta.m_is_session_started) + { + bLastInSession = false; + } + } +} diff --git a/src/core/data/infractions.hpp b/src/core/data/infractions.hpp index f46d46ed..a827bfac 100644 --- a/src/core/data/infractions.hpp +++ b/src/core/data/infractions.hpp @@ -7,7 +7,7 @@ namespace big { // Add new values to the bottom (for serialization) - DESYNC_PROTECTION, // do not use + DESYNC_PROTECTION, BREAKUP_KICK_DETECTED, // do not use LOST_CONNECTION_KICK_DETECTED, // do not use SPOOFED_ROCKSTAR_ID, // do not use diff --git a/src/function_types.hpp b/src/function_types.hpp index 59e12cfa..6197bdc1 100644 --- a/src/function_types.hpp +++ b/src/function_types.hpp @@ -211,4 +211,6 @@ namespace big::functions using received_clone_remove = void (*)(CNetworkObjectMgr*, CNetGamePlayer*, CNetGamePlayer*, int16_t, uint32_t); using can_create_vehicle = bool (*)(); + + using get_unk_weapon = void* (*) (CPed*); } diff --git a/src/gta_pointers.hpp b/src/gta_pointers.hpp index 1ad4d43c..1439120e 100644 --- a/src/gta_pointers.hpp +++ b/src/gta_pointers.hpp @@ -372,6 +372,11 @@ namespace big functions::can_create_vehicle m_can_create_vehicle; PVOID m_format_int; + + PVOID m_searchlight_crash; + functions::get_unk_weapon m_get_unk_weapon; + + GenericPool** m_clone_create_pool; // this is not a normal pool PVOID m_write_physical_script_game_state_data_node; }; diff --git a/src/hooking/hooking.cpp b/src/hooking/hooking.cpp index 073f08be..acc0835d 100644 --- a/src/hooking/hooking.cpp +++ b/src/hooking/hooking.cpp @@ -144,7 +144,10 @@ namespace big detour_hook_helper::add("FI", g_pointers->m_gta.m_format_int); + detour_hook_helper::add("SLC", g_pointers->m_gta.m_searchlight_crash); + detour_hook_helper::add("WPSGSDN", g_pointers->m_gta.m_write_physical_script_game_state_data_node); + g_hooking = this; } diff --git a/src/hooking/hooking.hpp b/src/hooking/hooking.hpp index c0b29c8b..66004e05 100644 --- a/src/hooking/hooking.hpp +++ b/src/hooking/hooking.hpp @@ -197,6 +197,8 @@ namespace big static void format_int(int64_t integer_to_format, char* format_string, size_t size_always_64, bool use_commas); + static void searchlight_crash(void* a1, CPed* ped); + static void write_physical_script_game_state_data_node(rage::CPhysical* this_ptr, CPhysicalScriptGameStateDataNode* node); }; diff --git a/src/hooks/misc/serialize_join_request_message.cpp b/src/hooks/misc/serialize_join_request_message.cpp index b6cb3a65..5d3c6f22 100644 --- a/src/hooks/misc/serialize_join_request_message.cpp +++ b/src/hooks/misc/serialize_join_request_message.cpp @@ -1,5 +1,6 @@ #include "gta_util.hpp" #include "hooking/hooking.hpp" +#include "services/players/player_service.hpp" #include #include diff --git a/src/hooks/player_management/assign_physical_index.cpp b/src/hooks/player_management/assign_physical_index.cpp index 0c46342b..82f1c496 100644 --- a/src/hooks/player_management/assign_physical_index.cpp +++ b/src/hooks/player_management/assign_physical_index.cpp @@ -146,6 +146,16 @@ namespace big { session::add_infraction(plyr, Infraction::SPOOFED_HOST_TOKEN); } + + if (g_player_service->get_self()->is_host() && plyr->get_net_data()->m_nat_type == 0) + { + session::add_infraction(plyr, Infraction::DESYNC_PROTECTION); + } + + if (plyr->is_host() && plyr->get_net_data()->m_nat_type == 0) + { + session::add_infraction(plyr, Infraction::DESYNC_PROTECTION); // some broken menus may do this + } } }); } diff --git a/src/hooks/protections/received_clone_create.cpp b/src/hooks/protections/received_clone_create.cpp index d4e1556d..30427fa9 100644 --- a/src/hooks/protections/received_clone_create.cpp +++ b/src/hooks/protections/received_clone_create.cpp @@ -1,6 +1,7 @@ #include "hooking/hooking.hpp" #include "services/players/player_service.hpp" #include "util/notify.hpp" +#include "gta/pools.hpp" namespace big { @@ -12,6 +13,13 @@ namespace big return; } + if (*g_pointers->m_gta.m_clone_create_pool && (*g_pointers->m_gta.m_clone_create_pool)->m_size < 2) + { + // We don't have enough memory to handle this + g_notification_service.push_warning("Protections", "Low net object pool size"); + return; + } + auto plyr = g_player_service->get_by_id(src->m_player_id); if (plyr && plyr->block_clone_create) [[unlikely]] diff --git a/src/hooks/protections/searchlight_crash.cpp b/src/hooks/protections/searchlight_crash.cpp new file mode 100644 index 00000000..30f2128c --- /dev/null +++ b/src/hooks/protections/searchlight_crash.cpp @@ -0,0 +1,13 @@ +#include "hooking/hooking.hpp" +#include "pointers.hpp" + +namespace big +{ + void hooks::searchlight_crash(void* a1, CPed* ped) + { + if (!ped || !g_pointers->m_gta.m_get_unk_weapon(ped)) [[unlikely]] + return; + + return g_hooking->get_original()(a1, ped); + } +} \ No newline at end of file diff --git a/src/pointers.cpp b/src/pointers.cpp index 8e09f6e1..fae2e2b7 100644 --- a/src/pointers.cpp +++ b/src/pointers.cpp @@ -1774,6 +1774,25 @@ namespace big g_pointers->m_gta.m_format_int = ptr.as(); } }, + // Searchlight Crash + { + "SLC", + "0F 29 70 E8 0F 29 78 D8 48 8B F9 48 8B CA", + [](memory::handle ptr) + { + g_pointers->m_gta.m_searchlight_crash = ptr.sub(0x1E).as(); + g_pointers->m_gta.m_get_unk_weapon = ptr.add(0x28).rip().as(); + } + }, + // Clone Create Pool + { + "CCP", + "48 8B 0D ? ? ? ? 45 33 C9 BA ? ? ? ? 41", + [](memory::handle ptr) + { + g_pointers->m_gta.m_clone_create_pool = ptr.add(3).rip().as(); + } + }, // Write Physical Script Game State Data Node { "WPSGSDN",