feat(anticheat): Anticheat bypass improvements (#2463)

- Add more metrics to bad_metrics set
- Add MM Filtering (This metric shouldnt be normally called anyway)
- Improve QD Hook to increase AC verifier delay when detected
- Removed gameskeleton hook in favor of patching ac at init
- Added tamperactions check to gameskeleton patcher
This commit is contained in:
yubie 2023-11-30 04:47:39 -05:00 committed by GitHub
parent 847a729918
commit 60887cb816
17 changed files with 173 additions and 133 deletions

View File

@ -3,7 +3,7 @@ include(FetchContent)
FetchContent_Declare(
gtav_classes
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
GIT_TAG 3c7763fcf996f53f891e40f12bbfa8115fd612a7
GIT_TAG 9e76175d28b3de21c24a69e71bee779d704d8304
GIT_PROGRESS TRUE
CONFIGURE_COMMAND ""
BUILD_COMMAND ""

View File

@ -64,7 +64,7 @@ namespace big::functions
using ptr_to_handle = Entity (*)(void*);
using handle_to_ptr = rage::CDynamicEntity* (*)(Entity);
using set_gravity_level = void(*)(int level);
using set_gravity_level = void (*)(int level);
using check_chat_profanity = int(__int64 chat_type, const char* input, const char** output);
using write_player_game_state_data_node = bool (*)(rage::netObject* plr, CPlayerGameStateDataNode* node);
@ -120,9 +120,9 @@ namespace big::functions
using start_get_presence_attributes = bool (*)(int profile_index, rage::rlScHandle* handle, int num_handles, rage::rlQueryPresenceAttributesContext** contexts, int count, rage::rlScTaskStatus* state);
using join_session_by_info = bool (*)(Network* network, rage::rlSessionInfo* info, int unk, int flags, rage::rlGamerHandle* handles, int handlecount);
using invite_player_by_gamer_handle = bool(*)(uint64_t config, rage::rlGamerHandle* handle, int unk1, int unk2, int unk3, int unk4);
using add_friend_by_gamer_handle = void(*)(rage::rlGamerHandle* handle, const char* unk);
using show_profile_by_gamer_handle = void(*)(rage::rlGamerHandle* handle);
using invite_player_by_gamer_handle = bool (*)(uint64_t config, rage::rlGamerHandle* handle, int unk1, int unk2, int unk3, int unk4);
using add_friend_by_gamer_handle = void (*)(rage::rlGamerHandle* handle, const char* unk);
using show_profile_by_gamer_handle = void (*)(rage::rlGamerHandle* handle);
using generate_uuid = bool (*)(uint64_t* uuid);
@ -194,9 +194,9 @@ namespace big::functions
using delete_vehicle = bool (*)(CVehicle* veh);
using delete_object = bool (*)(CObject* object, bool unk);
using decal_manager_remove = void(*)(PVOID manager, rage::fwEntity*, DWORD a3, DWORD64 a4, DWORD ignore_bitset);
using decal_manager_remove = void (*)(PVOID manager, rage::fwEntity*, DWORD a3, DWORD64 a4, DWORD ignore_bitset);
using remove_player_from_sender_list = bool(*)(void* list, uint64_t* rockstar_id);
using remove_player_from_sender_list = bool (*)(void* list, uint64_t* rockstar_id);
using get_ped_bone = bool(*)(CPed* ped_ptr, rage::fvector4& output, PedBones bone);
using get_ped_bone = bool (*)(CPed* ped_ptr, rage::fvector4& output, PedBones bone);
}

View File

@ -18,12 +18,11 @@ namespace rage
buffer(_buffer),
maxlen(_length)
{
unk0
= 0;
unk1 = 0;
curlen = 0;
unk4 = 1;
flags = 0;
unk0 = 0;
unk1 = 0;
curlen = 0;
unk4 = 1;
flags = 0;
}
inline char* get_string() const

View File

@ -1,7 +1,8 @@
#pragma once
#include <memory/handle.hpp>
#include "function_types.hpp"
#include <memory/handle.hpp>
class CCommunications;
class FriendRegistry;
class CNetworkPlayerMgr;
@ -27,6 +28,7 @@ namespace rage
class RageSecurity;
class netTime;
class rlGamerInfo;
struct game_skeleton;
}
template<typename T>
@ -71,6 +73,8 @@ namespace big
CPedFactory** m_ped_factory;
CNetworkPlayerMgr** m_network_player_mgr;
CNetworkObjectMgr** m_network_object_mgr;
rage::game_skeleton* m_game_skeleton;
void (*m_nullsub)();
functions::ptr_to_handle m_ptr_to_handle;
functions::handle_to_ptr m_handle_to_ptr;
@ -353,8 +357,6 @@ namespace big
bool* m_is_social_club_overlay_active;
PVOID m_game_skeleton_update;
functions::get_ped_bone m_get_ped_bone;
};
#pragma pack(pop)

View File

@ -1,7 +1,7 @@
#pragma once
#include "gta/script_thread.hpp"
#include "script/tlsContext.hpp"
#include "pointers.hpp"
#include "script/tlsContext.hpp"
#include <network/CNetworkPlayerMgr.hpp>
#include <ped/CPedFactory.hpp>

View File

@ -147,8 +147,6 @@ namespace big
detour_hook_helper::add<hooks::read_bits_single>("RBS", g_pointers->m_gta.m_read_bits_single);
detour_hook_helper::add<hooks::game_skeleton_update>("GSU", g_pointers->m_gta.m_game_skeleton_update);
g_hooking = this;
}

View File

@ -1,14 +1,14 @@
#pragma once
#include "MinHook.h"
#include "call_hook.hpp"
#include "common.hpp"
#include "detour_hook.hpp"
#include "gta/enums.hpp"
#include "gta/fwddec.hpp"
#include "gta/script_thread.hpp"
#include "gta/json_serializer.hpp"
#include "gta/script_thread.hpp"
#include "vmt_hook.hpp"
#include "vtable_hook.hpp"
#include "call_hook.hpp"
#include <network/netConnection.hpp>
@ -128,7 +128,7 @@ namespace big
static void serialize_parachute_task(__int64 info, rage::CSyncDataBase* serializer);
static int nt_query_virtual_memory(void* _this, HANDLE handle, PVOID base_addr, int info_class, MEMORY_BASIC_INFORMATION* info, int size, size_t* return_len);
static int queue_dependency(void* a1, int a2, void* dependency);
static int queue_dependency(void* a1, int a2, int64_t dependency);
static bool prepare_metric_for_sending(rage::json_serializer* bit_buffer, int unk, int time, rage::rlMetric* metric);
static bool http_start_request(void* request, const char* uri);
@ -185,7 +185,6 @@ namespace big
static bool sync_reader_serialize_array(void* _this, void* array, int size);
static bool remove_player_from_sender_list(void* list, uint64_t rockstar_id);
static void game_skeleton_update(__int64 skeleton, int type);
};
class minhook_keepalive

View File

@ -3,7 +3,7 @@
namespace big
{
const auto bad_metrics = std::unordered_set<std::string>({
const auto bad_metrics = std::unordered_set<std::string_view>({
"REPORTER",
"REPORT_INVALIDMODEL",
"MEM_NEW",
@ -36,6 +36,7 @@ namespace big
"GSINT",
"EARN",
"GARAGE_TAMPER",
"DUPE_DETECT",
"LAST_VEH",
"FAIL_SERV",
"CCF_UPDATE",
@ -43,25 +44,72 @@ namespace big
"COLLECTIBLE",
"FIRST_VEH",
"MM",
"RDEV",
"RQA",
"RANK_UP",
});
std::string hex_encode(std::string_view input) {
const char* hex_chars = "0123456789ABCDEF";
std::string output;
output.reserve(input.length() * 2); // Pre-allocate memory for efficiency
for (unsigned char c : input) {
output.push_back(hex_chars[c >> 4]); // Extract the high nibble (4 bits)
output.push_back(hex_chars[c & 0x0F]); // Extract the low nibble
}
return output;
}
std::string remove_module_from_mmlist(std::string_view input, std::string_view element_to_remove) {
std::string result(input);
std::string delimiter = "|";
size_t start_pos = 0;
while (true) {
size_t delimiter_pos = result.find(delimiter, start_pos);
if (delimiter_pos == std::string::npos) {
break;
}
std::string current_element = result.substr(start_pos, delimiter_pos - start_pos);
if (current_element == element_to_remove) {
result.erase(start_pos, delimiter_pos - start_pos + delimiter.length());
break;
}
start_pos = delimiter_pos + delimiter.length();
}
return result;
}
bool hooks::prepare_metric_for_sending(rage::json_serializer* serializer, int unk, int time, rage::rlMetric* metric)
{
const auto ret = g_hooking->get_original<prepare_metric_for_sending>()(serializer, unk, time, metric);
const auto is_bad_metric = bad_metrics.contains(metric->get_name());
char metric_json_buffer [256] {};
rage::json_serializer yim_serializer(metric_json_buffer, sizeof(metric_json_buffer));
metric->serialize(&yim_serializer);
const bool is_bad_metric = bad_metrics.contains(metric->get_name());
if (is_bad_metric)
{
LOG(WARNING) << "BAD METRIC: " << metric->get_name() << "; DATA: " << serializer->get_string();
LOG(WARNING) << "BAD METRIC: " << metric->get_name() << "; DATA: " << yim_serializer.get_string();
if(strcmp(metric->get_name(), "MM") == 0)
{
std::string data = std::string(reinterpret_cast<char*>(metric) + 0x18);
char module_name[MAX_PATH];
GetModuleFileNameA(g_hmodule, module_name, sizeof(module_name));
std::string encoded_module_name = hex_encode(std::filesystem::path(module_name).filename().string());
std::string result = remove_module_from_mmlist(data, encoded_module_name + "00");
if(result.size() != data.size())
LOG(INFO) << "Removed YimMenu DLL from MM metric";
strncpy(reinterpret_cast<char*>(metric) + 0x18, result.c_str(), 0x900);
return g_hooking->get_original<prepare_metric_for_sending>()(serializer, unk, time, metric);
}
return false;
}
if (!is_bad_metric && g.debug.logs.metric_logs)
{
LOG(INFO) << "METRIC: " << metric->get_name() << "; DATA: " << serializer->get_string();
LOG(INFO) << "METRIC: " << metric->get_name() << "; DATA: " << yim_serializer.get_string();
}
return ret;
return g_hooking->get_original<prepare_metric_for_sending>()(serializer, unk, time, metric);
}
}

View File

@ -1,63 +0,0 @@
#include "hooking.hpp"
// rage::gameSkeleton -> modes (does not derive from updateBase?) -> groups -> items
namespace big
{
class game_skeleton_update_group;
class game_skeleton_item;
#pragma pack(push, 8)
struct game_skeleton_update_mode
{
int m_type; // 0x00
game_skeleton_update_group* m_groups; // 0x08
game_skeleton_update_mode* m_next; // 0x10
};
static_assert(sizeof(game_skeleton_update_mode) == 0x18);
struct game_skeleton_update_group
{
virtual ~game_skeleton_update_group() = default;
virtual void run() = 0; // 0x08
char pad[0x10]; // 0x08
game_skeleton_update_group* m_next; // 0x18
game_skeleton_item* m_items; // 0x20
};
static_assert(sizeof(game_skeleton_update_group) == 0x28);
struct game_skeleton_item
{
virtual ~game_skeleton_item() = default;
virtual void run() = 0; // 0x08
char m_pad[0x8]; // 0x08
uint32_t m_hash; // 0x10
game_skeleton_item* m_next; // 0x18
};
static_assert(sizeof(game_skeleton_item) == 0x20);
#pragma pack(pop)
void hooks::game_skeleton_update(__int64 skeleton, int type)
{
for (auto mode = *(game_skeleton_update_mode**)(skeleton + 0x140); mode; mode = mode->m_next)
{
if (mode && mode->m_type == type)
{
for (auto group = mode->m_groups; group; group = group->m_next)
{
for (auto item = group->m_items; item; item = item->m_next)
{
if (item->m_hash != 0xA0F39FB6)
{
item->run();
}
}
}
break;
}
}
}
}

View File

@ -1,15 +1,17 @@
#include "hooking.hpp"
#include "pointers.hpp"
#include "security/ObfVar.hpp"
#include <Psapi.h>
namespace big
{
bool inline is_address_in_game_region(uint64_t address)
bool inline is_address_in_game_region(int64_t address)
{
static uint64_t moduleBase = NULL;
static uint64_t moduleSize = NULL;
if ((!moduleBase) || (!moduleSize))
if(!address)
return false;
static int64_t moduleBase = NULL;
static int64_t moduleSize = NULL;
if (!moduleBase || !moduleSize)
{
MODULEINFO info;
if (!GetModuleInformation(GetCurrentProcess(), GetModuleHandle(0), &info, sizeof(info)))
@ -19,31 +21,34 @@ namespace big
}
else
{
moduleBase = (uint64_t)GetModuleHandle(0);
moduleSize = (uint64_t)info.SizeOfImage;
moduleBase = (int64_t)GetModuleHandle(0);
moduleSize = (int64_t)info.SizeOfImage;
}
}
return address > moduleBase && address < (moduleBase + moduleSize);
}
bool is_jump(__int64 fptr)
struct ac_verifier
{
if (!is_address_in_game_region(fptr))
virtual ~ac_verifier() = 0;
virtual bool run() = 0;
rage::Obf32 m_last_time; // 0x8
rage::Obf32 m_delay; // 0x18
};
bool is_unwanted_dependency(int64_t cb)
{
int64_t f1 = *reinterpret_cast<int64_t*>(cb + 0x60);
int64_t f2 = *reinterpret_cast<int64_t*>(cb + 0x100);
int64_t f3 = *reinterpret_cast<int64_t*>(cb + 0x1A0);
if (!is_address_in_game_region(f1) || !is_address_in_game_region(f2) || !is_address_in_game_region(f3))
return false;
auto value = *(uint8_t*)(fptr);
return value == 0xE9;
}
bool is_unwanted_dependency(__int64 cb)
{
auto f1 = *(__int64*)(cb + 0x60);
auto f2 = *(__int64*)(cb + 0x100);
if (!is_address_in_game_region(f1) || (f2 && !is_address_in_game_region(f2)))
if(*reinterpret_cast<uint8_t*>(f1) != 0xE9)
return false;
return is_jump(f1) || is_jump(f2);
return true;
}
static bool nullsub()
@ -51,12 +56,16 @@ namespace big
return true; // returning false would cause the dependency to requeue
}
int hooks::queue_dependency(void* a1, int a2, void* dependency)
int hooks::queue_dependency(void* a1, int a2, int64_t dependency)
{
if (is_unwanted_dependency((__int64)dependency))
if (is_unwanted_dependency(dependency))
{
*(void**)((__int64)dependency + 0x60) = nullsub;
*(void**)((__int64)dependency + 0x100) = nullsub;
LOG(INFO) << "Blocking AC Verifier " << std::hex << *reinterpret_cast<int64_t*>(dependency + 0x60) - reinterpret_cast<int64_t>(GetModuleHandleA(0));
ac_verifier* verifier = reinterpret_cast<ac_verifier*>(dependency - 0x30);
verifier->m_delay = INT_MAX; // makes it so these won't queue in the future
*reinterpret_cast<void**>(dependency + 0x60) = nullsub;
*reinterpret_cast<void**>(dependency + 0x100) = nullsub;
*reinterpret_cast<void**>(dependency + 0x1A0) = nullsub;
}
return g_hooking->get_original<hooks::queue_dependency>()(a1, a2, dependency);

View File

@ -9,6 +9,7 @@
#include "lua/lua_manager.hpp"
#include "native_hooks/native_hooks.hpp"
#include "pointers.hpp"
#include "rage/gameSkeleton.hpp"
#include "renderer.hpp"
#include "script_mgr.hpp"
#include "services/api/api_service.hpp"
@ -36,6 +37,41 @@
#include "thread_pool.hpp"
#include "version.hpp"
namespace big
{
void disable_anticheat_skeleton()
{
for (rage::game_skeleton_update_mode* mode = g_pointers->m_gta.m_game_skeleton->m_update_modes; mode; mode = mode->m_next)
{
for (rage::game_skeleton_update_base* update_node = mode->m_head; update_node; update_node = update_node->m_next)
{
if (update_node->m_hash != RAGE_JOAAT("Common Main"))
continue;
rage::game_skeleton_update_group* group = reinterpret_cast<rage::game_skeleton_update_group*>(update_node);
for (rage::game_skeleton_update_base* group_child_node = group->m_head; group_child_node;
group_child_node = group_child_node->m_next)
{
// TamperActions is a leftover from the old AC, but still useful to block anyway
if (group_child_node->m_hash != 0xA0F39FB6 && group_child_node->m_hash != RAGE_JOAAT("TamperActions"))
continue;
//LOG(INFO) << "Patching problematic skeleton update";
reinterpret_cast<rage::game_skeleton_update_element*>(group_child_node)->m_function =
g_pointers->m_gta.m_nullsub;
}
break;
}
}
for (rage::skeleton_data& i : g_pointers->m_gta.m_game_skeleton->m_sys_data)
{
if (i.m_hash != 0xA0F39FB6 && i.m_hash != RAGE_JOAAT("TamperActions"))
continue;
//LOG(INFO) << "Patching problematic skeleton init/shutdown";
i.m_init_func = reinterpret_cast<uint64_t>(g_pointers->m_gta.m_nullsub);
i.m_shutdown_func = reinterpret_cast<uint64_t>(g_pointers->m_gta.m_nullsub);
}
}
}
BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
{
@ -75,6 +111,9 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
auto pointers_instance = std::make_unique<pointers>();
LOG(INFO) << "Pointers initialized.";
disable_anticheat_skeleton();
LOG(INFO) << "Disabled anticheat gameskeleton.";
auto byte_patch_manager_instance = std::make_unique<byte_patch_manager>();
LOG(INFO) << "Byte Patch Manager initialized.";
@ -85,8 +124,8 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
auto fiber_pool_instance = std::make_unique<fiber_pool>(11);
LOG(INFO) << "Fiber pool initialized.";
g_http_client.init(g_file_manager.get_project_file("./proxy_settings.json"));
LOG(INFO) << "HTTP Client initialized.";
g_http_client.init(g_file_manager.get_project_file("./proxy_settings.json"));
LOG(INFO) << "HTTP Client initialized.";
g_translation_service.init();
LOG(INFO) << "Translation Service initialized.";

View File

@ -4,6 +4,7 @@
#include "hooking.hpp"
#include "memory/all.hpp"
#include "rage/atSingleton.hpp"
#include "rage/gameSkeleton.hpp"
#include "sc_pointers_layout_info.hpp"
#include "security/RageSecurity.hpp"
@ -1722,13 +1723,22 @@ namespace big
g_pointers->m_gta.m_is_social_club_overlay_active = ptr.add(2).rip().as<bool*>();
}
},
// Game Skeleton Update
// Game Skeleton
{
"GSU",
"40 53 48 83 EC 20 48 8B 81 40 01",
"GS",
"48 8D 0D ? ? ? ? BA ? ? ? ? 74 05 BA ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? C6 05 ? ? ? ? ? 48 8D 0D ? ? ? ? BA ? ? ? ? 84 DB 75 05 BA ? ? ? ? E8 ? ? ? ? 48 8B CD C6 05 ? ? ? ? ? E8 ? ? ? ? 84",
[](memory::handle ptr)
{
g_pointers->m_gta.m_game_skeleton_update = ptr.as<PVOID>();
g_pointers->m_gta.m_game_skeleton = ptr.add(3).rip().as<rage::game_skeleton*>();
}
},
// Nullsub
{
"NS",
"C3",
[](memory::handle ptr)
{
g_pointers->m_gta.m_nullsub = ptr.as<void(*)()>();
}
},
// Get Ped Bone

View File

@ -7,7 +7,7 @@ namespace big
{
public:
constexpr script_global(std::size_t index) :
m_index(index)
m_index(index)
{
}
@ -35,6 +35,5 @@ namespace big
private:
void* get() const;
std::size_t m_index;
};
}

View File

@ -18,8 +18,8 @@ namespace big
}
script_local::script_local(std::size_t index) :
m_index(index),
m_stack(nullptr)
m_index(index),
m_stack(nullptr)
{
}

View File

@ -2,10 +2,10 @@
#include "common.hpp"
#include "gta/script_thread.hpp"
#include "script/tlsContext.hpp"
#include "gta_util.hpp"
#include "invoker.hpp"
#include "pointers.hpp"
#include "script/tlsContext.hpp"
namespace big
{

View File

@ -1,7 +1,7 @@
#pragma once
#include "common.hpp"
#include "script.hpp"
#include "lua/lua_manager.hpp"
#include "script.hpp"
namespace big
{

View File

@ -4,8 +4,8 @@ namespace big
{
thread_pool::thread_pool(const std::size_t preallocated_thread_count) :
m_accept_jobs(true),
m_allocated_thread_count(preallocated_thread_count),
m_busy_threads(0)
m_allocated_thread_count(preallocated_thread_count),
m_busy_threads(0)
{
rescale_thread_pool();