Improved detour handler, cleaned up hooks, and improved RCS hook (#399)

* Removed player join/leave for assign physical index, added new stuff to RCS, and improved joaating functions because it was driving me nuts.
* Improved the detour handler, got it down to 15ms or so for hooking. and added the sync options in the view
* Added includes to RCS
* Added a check on GGO to ensure it's not null
* Updated GTAV-Classes submodule
* Added CObject header file
* Changed a native to a pointer, when such changes didn't need to be done.
* Changed string on hooking
* Fixed the enum, was unsigned so removed -1 as unknown object to out of range sync.
* Added IDP Enable/Disable (#402)
This commit is contained in:
Forever Gone 2022-08-09 14:39:55 -04:00 committed by GitHub
parent 930b7eef60
commit 954b7673ee
15 changed files with 175 additions and 218 deletions

View File

@ -82,6 +82,8 @@ namespace big
pair send_net_info_to_lobby{};
pair transaction_rate_limit{};
pair mismatch_sync_type{};
pair out_of_allowed_range_sync_type{};
pair invalid_sync{};
};
@ -707,6 +709,8 @@ namespace big
},
{ "send_net_info_to_lobby", return_notify_pair(g->notifications.send_net_info_to_lobby) },
{ "transaction_rate_limit", return_notify_pair(g->notifications.transaction_rate_limit) },
{ "mismatch_sync_type", return_notify_pair(g->notifications.mismatch_sync_type) },
{ "out_of_allowed_range_sync_type", return_notify_pair(g->notifications.out_of_allowed_range_sync_type) },
{ "invalid_sync", return_notify_pair(g->notifications.invalid_sync) }
}
},

View File

@ -12,49 +12,26 @@ namespace big
m_detour(detour)
{
fix_hook_address();
if (auto status = MH_CreateHook(m_target, m_detour, &m_original); status == MH_OK)
{
LOG(INFO) << "Created hook '" << m_name << "'.";
}
else
{
if (auto status = MH_CreateHook(m_target, m_detour, &m_original); status != MH_OK)
throw std::runtime_error(fmt::format("Failed to create hook '{}' at 0x{:X} (error: {})", m_name, uintptr_t(m_target), MH_StatusToString(status)));
}
}
detour_hook::~detour_hook() noexcept
{
if (m_target)
{
MH_RemoveHook(m_target);
}
LOG(INFO) << "Removed hook '" << m_name << "'.";
if (auto status = MH_RemoveHook(m_target); status != MH_OK)
LOG(FATAL) << "Failed to remove hook '" << m_name << "' at 0x" << HEX_TO_UPPER(uintptr_t(m_target)) << "(error: " << m_name << ")";
}
void detour_hook::enable()
{
if (auto status = MH_EnableHook(m_target); status == MH_OK)
{
LOG(INFO) << "Enabled hook '" << m_name << "'.";
}
else
{
if (auto status = MH_QueueEnableHook(m_target); status != MH_OK)
throw std::runtime_error(fmt::format("Failed to enable hook 0x{:X} ({})", uintptr_t(m_target), MH_StatusToString(status)));
}
}
void detour_hook::disable()
{
if (auto status = MH_DisableHook(m_target); status == MH_OK)
{
LOG(INFO) << "Disabled hook '" << m_name << "'.";
}
else
{
if (auto status = MH_QueueDisableHook(m_target); status != MH_OK)
LOG(WARNING) << "Failed to disable hook '" << m_name << "'.";
}
}
DWORD exp_handler(PEXCEPTION_POINTERS exp, std::string const& name)

View File

@ -48,7 +48,5 @@ namespace big::functions
using get_sync_tree_for_type = int64_t(*)(CNetworkObjectMgr* mgr, uint16_t sync_type);
using get_net_object = rage::netObject*(*)(CNetworkObjectMgr* mgr, int16_t id, bool unk3);
using get_net_object_for_player = rage::netObject*(*)(CNetworkObjectMgr*, int16_t, CNetGamePlayer*, bool);
//Sync signatures END
}

View File

@ -3,6 +3,24 @@
constexpr auto MAX_PLAYERS = 32;
enum eObjType : uint16_t {
carObjType = 0,
bikeObjType = 1,
boatObjType = 2,
doorObjType = 3,
heliObjType = 4,
objType = 5,
pedObjType = 6,
pickupObjType = 7,
pickupPlacementObjType = 8,
planeObjType = 9,
submarineObjType = 10,
playerObjType = 11,
trailerObjType = 12,
trainObjType = 13,
unkObjType14 = 14,
unkObjType = 69
};
enum class ControllerInputs : std::uint32_t
{
INPUT_NEXT_CAMERA,

View File

@ -7,38 +7,24 @@
namespace rage
{
using joaat_t = std::uint32_t;
inline constexpr char joaat_to_lower(char c)
{
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
}
inline constexpr char joaat_to_lower(char c) { return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; }
template <std::size_t CharCount>
struct constexpr_joaat
{
struct constexpr_joaat {
char data[CharCount];
template <std::size_t... Indices>
constexpr constexpr_joaat(const char *str, std::index_sequence<Indices...>) :
data{ (str[Indices])... }
{
}
constexpr constexpr_joaat(const char *str, std::index_sequence<Indices...>) : data{ (str[Indices])... } {}
constexpr joaat_t operator()()
{
joaat_t hash = 0;
for (std::size_t i = 0; i < CharCount; ++i)
{
for (std::size_t i = 0; i < CharCount; ++i) {
hash += joaat_to_lower(data[i]);
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
};
@ -46,39 +32,17 @@ namespace rage
inline joaat_t joaat(std::string_view str)
{
joaat_t hash = 0;
for (char c : str)
{
for (auto c : str) {
hash += joaat_to_lower(c);
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
inline joaat_t joaat(const char *str)
{
joaat_t hash = 0;
while (*str)
{
hash += joaat_to_lower(*(str++));
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
}
#define RAGE_JOAAT_IMPL(str) (::rage::constexpr_joaat<sizeof(str) - 1>((str), std::make_index_sequence<sizeof(str) - 1>())())
#define RAGE_JOAAT(str) (std::integral_constant<rage::joaat_t, RAGE_JOAAT_IMPL(str)>::value)
#define RAGE_JOAAT(str) (std::integral_constant<rage::joaat_t, RAGE_JOAAT_IMPL(str)>::value)

View File

@ -48,10 +48,8 @@ namespace big
// Send NET Info to Lobby
m_send_net_info_to_lobby("SNITL", g_pointers->m_send_net_info_to_lobby, &hooks::send_net_info_to_lobby),
// Player Has Joined
m_player_has_joined_hook("PHJ", g_pointers->m_player_has_joined, &hooks::player_join),
// Player Has Left
m_player_has_left_hook("PHL", g_pointers->m_player_has_left, &hooks::player_leave),
// Assign Physical Index
m_assign_physical_index_hook("API", g_pointers->m_assign_physical_index, &hooks::assign_physical_index),
// Receive Net Message
m_receive_net_message_hook("RNM", g_pointers->m_receive_net_message, &hooks::receive_net_message),
// Received clone sync
@ -79,29 +77,22 @@ namespace big
m_og_wndproc = WNDPROC(SetWindowLongPtrW(g_pointers->m_hwnd, GWLP_WNDPROC, LONG_PTR(&hooks::wndproc)));
m_run_script_threads_hook.enable();
m_get_label_text.enable();
m_gta_thread_start_hook.enable();
m_gta_thread_kill_hook.enable();
m_network_group_override.enable();
m_network_player_mgr_init_hook.enable();
m_network_player_mgr_shutdown_hook.enable();
m_player_has_joined_hook.enable();
m_player_has_left_hook.enable();
m_assign_physical_index_hook.enable();
m_received_event_hook.enable();
m_is_dlc_present_hook.enable();
m_send_net_info_to_lobby.enable();
m_receive_net_message_hook.enable();
m_get_network_event_data_hook.enable();
m_received_clone_sync_hook.enable();
MH_ApplyQueued();
m_enabled = true;
}
@ -110,52 +101,32 @@ namespace big
m_enabled = false;
m_received_clone_sync_hook.disable();
m_get_network_event_data_hook.disable();
m_receive_net_message_hook.disable();
m_send_net_info_to_lobby.disable();
m_received_event_hook.disable();
m_player_has_joined_hook.disable();
m_player_has_left_hook.disable();
m_is_dlc_present_hook.disable();
m_assign_physical_index_hook.disable();
m_network_player_mgr_init_hook.disable();
m_network_player_mgr_shutdown_hook.disable();
m_network_group_override.disable();
m_gta_thread_kill_hook.disable();
m_gta_thread_start_hook.disable();
m_get_label_text.disable();
m_run_script_threads_hook.disable();
MH_ApplyQueued();
SetWindowLongPtrW(g_pointers->m_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_og_wndproc));
m_swapchain_hook.disable();
}
minhook_keepalive::minhook_keepalive()
{
MH_Initialize();
}
minhook_keepalive::~minhook_keepalive()
{
MH_Uninitialize();
}
bool hooks::run_script_threads(std::uint32_t ops_to_execute)
{
TRY_CLAUSE
{
if (g_running)
{
g_script_mgr.tick();
}
return g_hooking->m_run_script_threads_hook.get_original<functions::run_script_threads>()(ops_to_execute);
} EXCEPT_CLAUSE
return false;

View File

@ -6,6 +6,8 @@
#include "gta/script_thread.hpp"
#include "script_hook.hpp"
#include "vmt_hook.hpp"
#include "MinHook.h"
#include "gta/enums.hpp"
namespace big
{
@ -31,9 +33,6 @@ namespace big
static void network_group_override(std::int64_t a1, std::int64_t a2, std::int64_t a3);
static void player_join(CNetworkObjectMgr* _this, CNetGamePlayer* net_player);
static void player_leave(CNetworkObjectMgr* _this, CNetGamePlayer* net_player);
static bool is_dlc_present(Hash dlc_hash);
static void received_event(
@ -55,14 +54,23 @@ namespace big
static bool receive_net_message(void* netConnectionManager, void* a2, rage::netConnection::InFrame* frame);
static void get_network_event_data(int64_t unk, rage::CEventNetwork* net_event);
static void* assign_physical_index(CNetworkPlayerMgr* netPlayerMgr, CNetGamePlayer* player, uint8_t new_index);
//SYNC
static int64_t received_clone_sync(CNetworkObjectMgr* mgr, CNetGamePlayer* src, CNetGamePlayer* dst, uint16_t sync_type, uint16_t obj_id, rage::datBitBuffer* bufer, uint16_t unk, uint32_t timestamp);
static int64_t received_clone_sync(CNetworkObjectMgr* mgr, CNetGamePlayer* src, CNetGamePlayer* dst, eObjType sync_type, uint16_t obj_id, rage::datBitBuffer* bufer, uint16_t unk, uint32_t timestamp);
};
struct minhook_keepalive
class minhook_keepalive
{
minhook_keepalive();
~minhook_keepalive();
public:
minhook_keepalive()
{
MH_Initialize();
}
~minhook_keepalive()
{
MH_Uninitialize();
}
};
class hooking
@ -95,8 +103,7 @@ namespace big
detour_hook m_network_group_override;
detour_hook m_player_has_joined_hook;
detour_hook m_player_has_left_hook;
detour_hook m_assign_physical_index_hook;
detour_hook m_is_dlc_present_hook;

View File

@ -0,0 +1,41 @@
#include "hooking.hpp"
#include "services/players/player_service.hpp"
#include "util/notify.hpp"
namespace big
{
void* hooks::assign_physical_index(CNetworkPlayerMgr* netPlayerMgr, CNetGamePlayer* player, uint8_t new_index)
{
auto returnResult = g_hooking->m_assign_physical_index_hook.get_original<decltype(&hooks::assign_physical_index)>()(netPlayerMgr, player, new_index);
if (new_index == 0xFF) {
g_player_service->player_leave(player);
if (const rage::netPlayerData* net_player_data = player->get_net_data(); net_player_data)
{
if (g->notifications.player_leave.log)
LOG(INFO) << "Player left '" << net_player_data->m_name
<< "' freeing slot #" << (int)player->m_player_id
<< " with Rockstar ID: " << net_player_data->m_rockstar_id2;
if (g->notifications.player_leave.notify)
g_notification_service->push("Player Left", fmt::format("{} freeing slot #{} with Rockstar ID: {}", net_player_data->m_name, player->m_player_id, net_player_data->m_rockstar_id2));
}
return returnResult;
}
g_player_service->player_join(player);
if (const rage::netPlayerData* net_player_data = player->get_net_data(); net_player_data)
{
if (g->notifications.player_join.above_map)
notify::player_joined(player);
if (g->notifications.player_join.log)
LOG(INFO) << "Player joined '" << net_player_data->m_name
<< "' allocating slot #" << (int)player->m_player_id
<< " with Rockstar ID: " << net_player_data->m_rockstar_id2;
if (g->notifications.player_join.notify)
g_notification_service->push("Player Joined", fmt::format("{} taking slot #{} with Rockstar ID: {}", net_player_data->m_name, player->m_player_id, net_player_data->m_rockstar_id2));
}
return returnResult;
}
}

View File

@ -1,27 +0,0 @@
#include "hooking.hpp"
#include "services/players/player_service.hpp"
#include "util/notify.hpp"
namespace big
{
void hooks::player_join(CNetworkObjectMgr* _this, CNetGamePlayer* net_player)
{
g_player_service->player_join(net_player);
if (const rage::netPlayerData* net_player_data = net_player->get_net_data(); net_player_data)
{
if (g->notifications.player_join.above_map)
notify::player_joined(net_player);
if (g->notifications.player_join.log)
LOG(INFO) << "Player joined '" << net_player_data->m_name
<< "' taking slot #" << (int)net_player->m_player_id
<< " with Rockstar ID: " << net_player_data->m_rockstar_id2;
if (g->notifications.player_join.notify)
g_notification_service->push("Player Joined", fmt::format("{} taking slot #{} with Rockstar ID: {}", net_player_data->m_name, net_player->m_player_id, net_player_data->m_rockstar_id2));
}
return g_hooking->m_player_has_joined_hook.get_original<decltype(&hooks::player_join)>()(_this, net_player);
}
}

View File

@ -1,23 +0,0 @@
#include "hooking.hpp"
#include "services/players/player_service.hpp"
namespace big
{
void hooks::player_leave(CNetworkObjectMgr* _this, CNetGamePlayer* net_player)
{
g_player_service->player_leave(net_player);
if (const rage::netPlayerData* net_player_data = net_player->get_net_data(); net_player_data)
{
if (g->notifications.player_leave.log)
LOG(INFO) << "Player left '" << net_player_data->m_name
<< "' freeing slot #" << (int)net_player->m_player_id
<< " with Rockstar ID: " << net_player_data->m_rockstar_id2;
if (g->notifications.player_leave.notify)
g_notification_service->push("Player Left", fmt::format("{} freeing slot #{} with Rockstar ID: {}", net_player_data->m_name, net_player->m_player_id, net_player_data->m_rockstar_id2));
}
return g_hooking->m_player_has_left_hook.get_original<decltype(&hooks::player_leave)>()(_this, net_player);
}
}

View File

@ -1,5 +1,9 @@
#include "hooking.hpp"
#include "core/globals.hpp"
#include "natives.hpp"
#include "network/netObject.hpp"
#include "rage/fwEntity.hpp"
#include "base/CObject.hpp"
namespace big {
@ -25,38 +29,69 @@ namespace big {
break;
*/
int64_t hooks::received_clone_sync(CNetworkObjectMgr* mgr, CNetGamePlayer* src, CNetGamePlayer* dst, uint16_t sync_type, uint16_t obj_id, rage::datBitBuffer* buffer, uint16_t unk, uint32_t timestamp) {
auto sync_tree = g_pointers->m_get_sync_tree_for_type(mgr, sync_type);
auto tree_name = g_pointers->m_get_sync_type_info(sync_type, 0);
auto net_obj = g_pointers->m_get_net_object(mgr, obj_id, true);
bool invalidsync = false;
if(!net_obj) net_obj = g_pointers->m_get_net_object_for_player(mgr, obj_id, src, true);
if (!net_obj) return 2;
if (!sync_tree || sync_type < 0 || sync_type > 14) invalidsync = true;
if (net_obj->m_object_type != sync_type) invalidsync = true;
//TO BE ADDED
//Node specific entity type checks
if (invalidsync) {
if (g->notifications.invalid_sync.log)
LOG(WARNING) << "Invalid sync: " << "Type: " << sync_type << " Tree name: " << tree_name << " From: " << src->get_name();
if (g->notifications.invalid_sync.notify)
g_notification_service->push_warning(fmt::format("Invalid Sync from {}", src->get_name()), fmt::format("Type {} in sync tree {}", sync_type, tree_name));
return 2;
const CVehicleModelInfo* get_model_data(rage::joaat_t hash) {
auto modelTble = g_pointers->m_model_table;
for (auto i = modelTble->m_lookup_table[hash % modelTble->m_lookup_key]; i; i = i->m_next)
{
if (i->m_hash == hash)
{
if (const auto model = modelTble->m_data[i->m_idx]; model)
{
return reinterpret_cast<CVehicleModelInfo*>(model);
}
}
}
return nullptr;
}
int64_t hooks::received_clone_sync(CNetworkObjectMgr* mgr, CNetGamePlayer* src, CNetGamePlayer* dst, eObjType sync_type, uint16_t obj_id, rage::datBitBuffer* buffer, uint16_t unk, uint32_t timestamp) {
if (auto sync_tree = g_pointers->m_get_sync_tree_for_type(mgr, sync_type); sync_tree && *g_pointers->m_is_session_started)
{
if (auto net_obj = g_pointers->m_get_net_object(mgr, obj_id, true); net_obj)
{
auto tree_name = g_pointers->m_get_sync_type_info(sync_type, 0);
bool invalidsync = false;
auto result = g_hooking->m_received_clone_sync_hook.get_original<decltype(&received_clone_sync)>()(mgr, src, dst, sync_type, obj_id, buffer, unk, timestamp);
if (sync_type < eObjType::carObjType || sync_type > eObjType::unkObjType14)
{
if (g->notifications.out_of_allowed_range_sync_type.log)
LOG(WARNING) << "Out of range sync: " << "Type: " << sync_type << " Tree name: " << tree_name << " From: " << src->get_name();
if (g->notifications.out_of_allowed_range_sync_type.notify)
g_notification_service->push_warning(fmt::format("Out Of Allowed Sync Range from {}", src->get_name()), fmt::format("Type {} in sync tree {}", sync_type, tree_name));
}
else if (net_obj->m_object_type != sync_type)
{
if (g->notifications.mismatch_sync_type.log)
LOG(WARNING) << "Mismatch sync: " << "Type: " << sync_type << " Tree name: " << tree_name << " From: " << src->get_name();
if (g->notifications.mismatch_sync_type.notify)
g_notification_service->push_warning(fmt::format("Mismatch Sync from {}", src->get_name()), fmt::format("Type {} in sync tree {}", sync_type, tree_name));
return result;
return 2;
}
else if (auto game_obj = net_obj->GetGameObject(); game_obj)
{
if (auto model_info = game_obj->m_model_info)
{
if (!STREAMING::IS_MODEL_VALID(model_info->m_model_hash))
{
return 2;
}
else if (reinterpret_cast<CVehicleModelInfo*>(model_info)->m_vehicle_type != get_model_data(model_info->m_model_hash)->m_vehicle_type)
{
return 2;
}
else if (model_info->m_model_type != get_model_data(model_info->m_model_hash)->m_model_type)
{
return 2;
}
}
}
}
else if (sync_type != eObjType::pedObjType) //We don't want to not sync a player, so we ensure it's not a ped
{
return 2;
}
}
return g_hooking->m_received_clone_sync_hook.get_original<decltype(&received_clone_sync)>()(mgr, src, dst, sync_type, obj_id, buffer, unk, timestamp);
}

View File

@ -245,18 +245,6 @@ namespace big
m_network_object_mgr = ptr.add(3).rip().as<CNetworkObjectMgr**>();
});
// Player Has Joined
main_batch.add("PHJ", "48 8B CA 48 8B F2 FF 50 18 4C 8D 05", [this](memory::handle ptr)
{
m_player_has_joined = ptr.sub(0x26).as<PVOID>();
});
// Player Has Left
main_batch.add("PHL", "4C 8B F1 48 8B CA 48 8B EA FF 50 18 4C 8D 05", [this](memory::handle ptr)
{
m_player_has_left = ptr.sub(0x26).as<PVOID>();
});
// Network Player Mgr Init
main_batch.add("NPMI", "41 56 48 83 EC ? 48 8B F1 B9 ? ? ? ? 49 8B F9 41 8B E8 4C 8B F2 E8", [this](memory::handle ptr)
{
@ -310,7 +298,13 @@ namespace big
// Get Network Event Data
main_batch.add("GNED", "53 43 52 49 50 54 5F 4E 45 54 57 4F 52 4B", [this](memory::handle ptr)
{
m_get_network_event_data = *ptr.sub(0x38).as<void**>();
m_get_network_event_data = *ptr.sub(0x38).as<PVOID*>();
});
// Assign Physical Index
main_batch.add("API", "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC 20 41 8A E8", [this](memory::handle ptr)
{
m_assign_physical_index = ptr.as<PVOID>();
});
// Received clone sync & Get sync tree for type & Get net object for player & Get sync type info & Get net object
@ -318,7 +312,6 @@ namespace big
{
m_received_clone_sync = ptr.sub(0x1D).as<decltype(m_received_clone_sync)>();
m_get_sync_tree_for_type = ptr.add(0x14).rip().as<decltype(m_get_sync_tree_for_type)>(); // 0F B7 CA 83 F9 07 .as()
m_get_net_object_for_player = ptr.add(0x60).rip().as<decltype(m_get_net_object_for_player)>(); // 41 80 78 ? FF 74 2D 41 0F B6 40 .as()
m_get_net_object = ptr.add(0x76).rip().as<decltype(m_get_net_object)>(); // E8 ? ? ? ? 0F B7 53 7C .add(1).rip().as()
m_get_sync_type_info = ptr.add(0x8C).rip().as<decltype(m_get_sync_type_info)>(); // 44 0F B7 C1 4C 8D 0D .as()
});

View File

@ -62,9 +62,6 @@ namespace big
PVOID m_network_player_mgr_init;
PVOID m_network_player_mgr_shutdown;
PVOID m_player_has_joined{};
PVOID m_player_has_left{};
functions::get_gameplay_cam_coords m_get_gameplay_cam_coords;
functions::give_pickup_rewards m_give_pickup_rewards{};
@ -94,13 +91,13 @@ namespace big
functions::get_sync_tree_for_type m_get_sync_tree_for_type{};
functions::get_sync_type_info m_get_sync_type_info{};
functions::get_net_object m_get_net_object{};
functions::get_net_object_for_player m_get_net_object_for_player{};
//Sync Signatures END
PVOID m_send_net_info_to_lobby{};
PVOID m_receive_net_message{};
PVOID m_get_network_event_data{};
PVOID m_assign_physical_index{};
};
inline pointers* g_pointers{};

View File

@ -96,6 +96,8 @@ namespace big
draw_pair_option("Reports", g->notifications.reports);
draw_pair_option("Transaction Error / Rate Limit", g->notifications.transaction_rate_limit);
draw_pair_option("Mismatch sync type", g->notifications.mismatch_sync_type);
draw_pair_option("Out of allowed range sync type", g->notifications.out_of_allowed_range_sync_type);
draw_pair_option("Invalid sync", g->notifications.invalid_sync);
}

2
vendor/GTAV-Classes vendored

@ -1 +1 @@
Subproject commit ba4cd0a8466d6fb2a8095393ec57cd681189d17a
Subproject commit b28a988a660bcdbf93df67befa9646b300b00279