Files
GTASource/game/security/plugins/clockguard.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

382 lines
12 KiB
C++

#include "security/ragesecengine.h"
#if USE_RAGESEC
#include "file/file_config.h"
#include "net/status.h"
#include "net/task.h"
#include "security/ragesecplugin.h"
#include "security/ragesecenginetasks.h"
#include "security/ragesecgameinterface.h"
#include "script/thread.h"
#include "clockguard.h"
#include "system/xtl.h"
#include "rline/rltelemetry.h"
using namespace rage;
#if RAGE_SEC_TASK_CLOCK_GUARD
#include <winsock2.h>
#include <ws2tcpip.h>
// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")
RAGE_DEFINE_SUBCHANNEL(net, clockguard, DIAG_SEVERITY_DEBUG3)
#undef __net_channel
#define __net_channel net_clockguard
#pragma warning( disable : 4100)
#if !__NO_OUTPUT
#define CLOCK_GUARD_POLLING_MS 5*1000;
#else
#define CLOCK_GUARD_POLLING_MS 60*1000*5
#endif
#define CLOCK_GUARD_ID 0x4A729ECB
static unsigned int sm_cgTrashValue = 0;
// Forward declare?
bool ClockGuardPlugin_Work()
{
//@@: location CLOCKGUARDPLUGIN_WORK_ENTRY
bool isMultiplayer =
NetworkInterface::IsAnySessionActive()
&& NetworkInterface::IsGameInProgress();
//@@: range CLOCKGUARDPLUGIN_WORK_BODY {
// Empty, for now, so I can fill this on a different CL
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
int minuteMod = now->tm_min % 60;
#if !__NO_OUTPUT
// Make testing easier for logging builds
minuteMod = now->tm_sec % 60;
#endif
if(minuteMod > 50)
{
gnetDebug3("0x%08X - 50 %s %d", CLOCK_GUARD_ID, NetworkInterface::IsAnySessionActive() ? "true" : "false", netInterface::GetNumPhysicalPlayers() );
//@@: location CLOCKGUARDPLUGIN_WORK_50
if( NetworkInterface::IsAnySessionActive() == false
&& (netInterface::GetNumPhysicalPlayers() > 0))
{
//@@: range CLOCKGUARDPLUGIN_WORK_50_BODY {
gnetAssertf(false, "0x%08X - NetworkInterface::IsAnySessionActive() tampered with.", CLOCK_GUARD_ID);
RageSecPluginGameReactionObject obj(
REACT_GAMESERVER,
CLOCK_GUARD_ID,
0xD1A33666,
0x3D1FD94E,
0xB5150D96);
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
sm_cgTrashValue *= sysTimer::GetSystemMsTime() - fwRandom::GetRandomNumber();
//@@: } CLOCKGUARDPLUGIN_WORK_50_BODY
}
}
else if(minuteMod > 40)
{
gnetDebug3("0x%08X - 40", CLOCK_GUARD_ID);
//@@: location CLOCKGUARDPLUGIN_WORK_40
if( NetworkInterface::IsGameInProgress())
{
//@@: range CLOCKGUARDPLUGIN_WORK_40_BODY {
if(isMultiplayer && NetworkInterface::GetLocalPlayer()->GetPlayerInfo()->MaxArmour > 250)
{
gnetAssertf(false, "0x%08X - Armor is above allowed threshold (250).", CLOCK_GUARD_ID);
RageSecPluginGameReactionObject obj(
REACT_GAMESERVER,
CLOCK_GUARD_ID,
0x185E1ABF,
NetworkInterface::GetLocalPlayer()->GetPlayerInfo()->MaxArmour,
0x882C3BF1);
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
obj.type = REACT_TELEMETRY; rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
sm_cgTrashValue += sysTimer::GetSystemMsTime() ^ fwRandom::GetRandomNumber();
}
//@@: } CLOCKGUARDPLUGIN_WORK_40_BODY
}
}
else if(minuteMod > 30)
{
gnetDebug3("0x%08X - 30", CLOCK_GUARD_ID);
//@@: location CLOCKGUARDPLUGIN_WORK_30
const unsigned int buffLen = 256;
char telemBuff[buffLen] = {0};
rlRos::GetServiceHost("/Telemetry.asmx/SubmitCompressed", telemBuff, buffLen);
//@@: range CLOCKGUARDPLUGIN_WORK_30_BODY {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0){
gnetDebug3("0x%08X - Failed to WSAStartup", CLOCK_GUARD_ID);
}
hostent *telemRemoteHost = gethostbyname(telemBuff);
// Declare some local variables
bool isOnAWS = true;
bool connectedTelemHost = false;
int aliasCount = 0;
int addrListCount = 0;
SOCKET Socket;
SOCKADDR_IN SockAddr;
if(telemRemoteHost == NULL) {
gnetDebug3("0x%08X - Telemetry Remote Host is NULL %s %d", CLOCK_GUARD_ID, telemBuff, WSAGetLastError());
// This is yielding false positives, so let's set to true to avoid that...
connectedTelemHost = true;
}
else
{
// First, we're going to check to see if we have a non-aws host
isOnAWS = strncmp(telemBuff, telemRemoteHost->h_name, buffLen) != 0;
// Now lets look for our set of aliases. We should have at least 1
// alias, as the prod.*.rockstargames.com is the alias for the
// AWS address
for(char **pAlias = telemRemoteHost->h_aliases;*pAlias != 0; pAlias++)
{
aliasCount++;
}
gnetDebug3("0x%08X - %d aliases found", CLOCK_GUARD_ID, aliasCount);
// Now lets count our address list, and see what alternative
// IPs we've gotten back. We should have more than one.
while (telemRemoteHost->h_addr_list[addrListCount] != 0) {
addrListCount++;
}
gnetDebug3("0x%08X - %d addresses found", CLOCK_GUARD_ID, addrListCount);
Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
SockAddr.sin_port=htons(80);
SockAddr.sin_family=telemRemoteHost->h_addrtype;
SockAddr.sin_addr.s_addr = *((unsigned long*)telemRemoteHost->h_addr);
if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) != 0)
{
gnetDebug3("0x%08X - Telemetry Remote Host %s failed basic connection", CLOCK_GUARD_ID, telemBuff);
}
else
{
connectedTelemHost = true;
}
closesocket(Socket);
gnetDebug3(
"0x%08X - %-16s %s | %-16s %s | %-16s %s | %-16s %s",
CLOCK_GUARD_ID,
"AWS hostname?",
isOnAWS ? "true" : "false",
"1 Alias?",
aliasCount == 1 ? "true" : "false",
">1 IP?",
addrListCount >= 1 ? "true" : "false",
">Valid Connect?",
connectedTelemHost? "true" : "false");
}
char condcBuff[buffLen] = {0};
rlRos::GetServiceHost("conductor", condcBuff, buffLen);
hostent *condcRemoteHost = gethostbyname(condcBuff);
bool connectedConductor = false;
if(condcRemoteHost == NULL) {
gnetDebug3("0x%08X - Conductor Remote Host is NULL %s %d", CLOCK_GUARD_ID, condcBuff, WSAGetLastError());
// This is yielding false positives, so let's set to true to avoid that...
connectedConductor = true;
}
else
{
Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
SockAddr.sin_port=htons(80);
SockAddr.sin_family=condcRemoteHost->h_addrtype;
SockAddr.sin_addr.s_addr = *((unsigned long*)condcRemoteHost->h_addr);
if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) != 0)
{
gnetDebug3("0x%08X - Conductor Host %s failed basic connection", CLOCK_GUARD_ID, telemBuff);
}
else
{
connectedConductor = true;
}
closesocket(Socket);
}
WSACleanup();
// Check each, and report appropriately
if( (!isOnAWS || aliasCount!=1 || addrListCount ==1)
&& isMultiplayer
&& connectedConductor
&& !connectedTelemHost)
{
gnetDebug3(
"0x%08X - %-16s %s | %-16s %s | %-16s %s | %-16s %s",
CLOCK_GUARD_ID,
"AWS hostname?",
isOnAWS ? "true" : "false",
"1 Alias?",
aliasCount == 1 ? "true" : "false",
">1 IP?",
addrListCount >= 1 ? "true" : "false",
">Valid Connect?",
connectedTelemHost? "true" : "false");
gnetAssertf(false, "0x%08X - Failed connectivity test A to %s", CLOCK_GUARD_ID, telemBuff);
RageSecPluginGameReactionObject obj(
REACT_INVALID,
CLOCK_GUARD_ID,
0xD961F45B,
0x83383652,
0x2DDA3A2C);
obj.type = REACT_GAMESERVER; rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
}
sm_cgTrashValue *= sysTimer::GetSystemMsTime() + fwRandom::GetRandomNumber();
//@@: } CLOCKGUARDPLUGIN_WORK_30_BODY
}
else if(minuteMod > 20)
{
gnetDebug3("0x%08X - 20", CLOCK_GUARD_ID);
//@@: location CLOCKGUARDPLUGIN_WORK_20
const unsigned int buffLen = 256;
char telemBuff[buffLen] = {0};
rlRos::GetServiceHost("/Telemetry.asmx/SubmitCompressed", telemBuff, buffLen);
char condcBuff[buffLen] = {0};
rlRos::GetServiceHost("conductor", condcBuff, buffLen);
addrinfo *result = NULL;
struct addrinfo hints;
//@@: range CLOCKGUARDPLUGIN_WORK_20_BODY {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0){
gnetDebug3("0x%08X - Failed to WSAStartup", CLOCK_GUARD_ID);
}
DWORD dwRetval = getaddrinfo(condcBuff, 0, &hints, &result);
bool conductorHostValid = dwRetval==0;
if(conductorHostValid == false) { gnetDebug3("0x%08X - Failed to call GetAddrInfo on %s %d", CLOCK_GUARD_ID, condcBuff, WSAGetLastError());}
dwRetval = getaddrinfo(telemBuff, 0, &hints, &result);
bool telemHostValid = dwRetval==0;
if(telemHostValid == false) { gnetDebug3("0x%08X - Failed to call GetAddrInfo on %s %d", CLOCK_GUARD_ID, telemBuff, WSAGetLastError());}
int telemAddrCount = 0;
for (addrinfo *ptr = result; ptr != NULL; ptr = ptr->ai_next) {
telemAddrCount++;
}
gnetDebug3("0x%08X - Found %d addresses on GetAddrInfo to %s", CLOCK_GUARD_ID, telemAddrCount, condcBuff);
WSACleanup();
if(conductorHostValid && isMultiplayer && (telemAddrCount<=1 || telemHostValid == false))
{
gnetAssertf(false, "0x%08X - Failed connectivity test B to %s", CLOCK_GUARD_ID, telemBuff);
RageSecPluginGameReactionObject obj(
REACT_INVALID,
CLOCK_GUARD_ID,
0x84D6C1C5,
0x49DAE3DE,
0x61C8D725);
obj.type = REACT_GAMESERVER; rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
}
sm_cgTrashValue += sysTimer::GetSystemMsTime() * fwRandom::GetRandomNumber();
//@@: } CLOCKGUARDPLUGIN_WORK_20_BODY
}
else if(minuteMod > 10)
{
gnetDebug3("0x%08X - 10", CLOCK_GUARD_ID);
//@@: location CLOCKGUARDPLUGIN_WORK_10
bool manualIsMultiplayer = CNetwork::IsNetworkSessionValid() &&
NetworkInterface::IsGameInProgress() &&
(CNetwork::GetNetworkSession().IsSessionActive()
|| CNetwork::GetNetworkSession().IsTransitionActive()
|| CNetwork::GetNetworkSession().IsTransitionMatchmaking()
|| CNetwork::GetNetworkSession().IsRunningDetailQuery());
//@@: range CLOCKGUARDPLUGIN_WORK_10_BODY {
if(manualIsMultiplayer!=isMultiplayer)
{
//@@: location CLOCKGUARDPLUGIN_WORK_10_BODY_GPU_CRASH
gnetAssertf(false, "0x%08X - Failed manual check for IsMultiplayer()", CLOCK_GUARD_ID);
RageSecPluginGameReactionObject obj(
REACT_INVALID,
CLOCK_GUARD_ID,
0xDF97C343,
0xF580C6C2,
0x74351203);
obj.type = REACT_GAMESERVER;
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
RageSecInduceGpuCrash();
}
//@@: } CLOCKGUARDPLUGIN_WORK_10_BODY
}
else if(minuteMod > 0)
{
//@@: range CLOCKGUARDPLUGIN_WORK_00_BODY {
gnetDebug3("0x%08X - 00", CLOCK_GUARD_ID);
//@@: location CLOCKGUARDPLUGIN_WORK_00
sm_cgTrashValue ^= sysTimer::GetSystemMsTime() - fwRandom::GetRandomNumber();
//@@: } CLOCKGUARDPLUGIN_WORK_00_BODY
sm_cgTrashValue *= sysTimer::GetSystemMsTime() + fwRandom::GetRandomNumber();
}
return true;
//@@: } CLOCKGUARDPLUGIN_WORK_BODY
}
bool ClockGuardPlugin_OnSuccess()
{
gnetDebug3("0x%08X - OnSuccess", CLOCK_GUARD_ID);
// Make this different enough so the compiler doesn't collapse this with another function
//@@: location CLOCKGUARDPLUGIN_ONSUCCESS
sm_cgTrashValue += sysTimer::GetSystemMsTime() - fwRandom::GetRandomNumber();
return true;
}
bool ClockGuardPlugin_OnFailure()
{
gnetDebug3("0x%08X - OnFailure", CLOCK_GUARD_ID);
// Make this different enough so the compiler doesn't collapse this with another function
//@@: location CLOCKGUARDPLUGIN_ONFAILURE
sm_cgTrashValue *= sysTimer::GetSystemMsTime() - fwRandom::GetRandomNumber();
return true;
}
bool ClockGuardPlugin_Init()
{
// Since there's no real initialization here necessary, we can just assign our
// creation function, our destroy function, and just register the object
// accordingly
RageSecPluginParameters rs;
RageSecPluginExtendedInformation ei;
ei.startTime = sysTimer::GetSystemMsTime();
ei.frequencyInMs = CLOCK_GUARD_POLLING_MS;
rs.version.major = PLUGIN_VERSION_MAJOR;
rs.version.minor = PLUGIN_VERSION_MINOR;
rs.type = EXECUTE_PERIODIC;
rs.extendedInfo = ei;
rs.Create = NULL;
rs.Configure = NULL;
rs.Destroy = NULL;
rs.DoWork = &ClockGuardPlugin_Work;
rs.OnSuccess = &ClockGuardPlugin_OnSuccess;
rs.OnCancel = NULL;
rs.OnFailure = &ClockGuardPlugin_OnFailure;
// Register the function
// TO CREATE A NEW ID, GO TO WWW.RANDOM.ORG/BYTES AND GENERATE
// FOUR BYTES TO REPRESENT THE ID.
rageSecPluginManager::RegisterPluginFunction(&rs,CLOCK_GUARD_ID);
return true;
}
#pragma warning( error : 4100)
#endif // RAGE_SEC_TASK_CLOCK_GUARD
#endif // RAGE_SEC