Posted: . At: 11:24 AM. This was 4 months ago. Post ID: 18981
Page permalink. WordPress uses cookies, or tiny pieces of information stored on your computer, to verify who you are. There are cookies for logged in users and for commenters.
These cookies expire two weeks after they are set.


RockStar cheat detection software is very simplistic.


The Rockstar anti-cheat code is only looking for a running application named “Cheat Engine”. This only works in multiplayer modes, but this is not very good practice. You could just edit the Cheat Engine application to change the name and it would work. But maybe it would still be the same application running in RAM. If the data in a multiplayer game is server-sided, then you would not need to worry would you?

src\dev_ng\game\security\plugins\cedetectionplugin.cpp
u64 CEMetadataChecker::GetSystemTime()
{
	FILETIME ft;
	GetSystemTimeAsFileTime(&ft);
	return ((u64)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
}

bool CEMetadataChecker::GetSoftwareKey(HKEY& hKey) const
{
	const MODULE_ID ADVAPI32_MODULE_ID = HashString("Advapi32.dll").GetHash();
	const FUNCTION_ID REGOPENKEYEXA_FUNCTION_ID = HashString("RegOpenKeyExA").GetHash();
	const RegOpenKeyExAFn regOpenKeyExA = (RegOpenKeyExAFn)GetFunctionAddressByHash(
		ADVAPI32_MODULE_ID, REGOPENKEYEXA_FUNCTION_ID);

	char softwareCheatEngine[] = { 'S', 'O', 'F', 'T', 'W', 'A', 'R', 'E', '\\', 'C', 'h', 'e',
		'a', 't', ' ', 'E', 'n', 'g', 'i', 'n', 'e', '\0' };
	return SUCCEEDED(regOpenKeyExA(HKEY_CURRENT_USER, softwareCheatEngine, 0, KEY_READ, &hKey));
}

bool CEMetadataChecker::GetUsingTempDirectory(const HKEY& inKey, DWORD& outValue) const
{
	const MODULE_ID ADVAPI32_MODULE_ID = HashString("Advapi32.dll").GetHash();
	const FUNCTION_ID REGQUERYVALUEEXA_FUNCTION_ID = HashString("RegQueryValueExA").GetHash();
	const RegQueryValueExAFn regQueryValueExA = (RegQueryValueExAFn)GetFunctionAddressByHash(
		ADVAPI32_MODULE_ID, REGQUERYVALUEEXA_FUNCTION_ID);

	DWORD dwData;
	DWORD cbData = sizeof(DWORD);
	// Don't use tempdir
	const char szValueName[] = { 'D', 'o', 'n', '\'', 't', ' ', 'u', 's', 'e', ' ', 't', 'e', 'm',
		'p', 'd', 'i', 'r', '\0' };

	if (SUCCEEDED(regQueryValueExA(inKey, szValueName, NULL, NULL, (LPBYTE)&dwData, &cbData)))
	{
		outValue = dwData;
		return true;
	}

	return false;
}

bool CEMetadataChecker::GetCustomDataDirectory(const HKEY& inKey, atString& outValue) const
{
	const MODULE_ID ADVAPI32_MODULE_ID = HashString("Advapi32.dll").GetHash();
	const FUNCTION_ID REGQUERYVALUEEXA_FUNCTION_ID = HashString("RegQueryValueExA").GetHash();
	const RegQueryValueExAFn regQueryValueExA = (RegQueryValueExAFn)GetFunctionAddressByHash(
		ADVAPI32_MODULE_ID, REGQUERYVALUEEXA_FUNCTION_ID);

	// Scanfolder
	char relativePath[] = { 'C', 'h', 'e', 'a', 't', ' ', 'E', 'n', 'g', 'i', 'n', 'e', '\0' };
	char separator[] = { '\\', '\0' };
	char szValueName[] = { 'S', 'c', 'a', 'n', 'f', 'o', 'l', 'd', 'e', 'r', '\0' };
	char szValue[4096] = { 0 };
	DWORD cbData = sizeof(szValue);

	if (!SUCCEEDED(regQueryValueExA(inKey, szValueName, NULL, NULL, (LPBYTE)&szValue, &cbData)))
		return false;

	outValue = szValue;
	if (!StringEndsWith(outValue, separator))
		outValue += separator;

	outValue += relativePath;
	return true;
}

bool CEMetadataChecker::GetDefaultDataDirectory(atString& outPath) const
{
	char appDataRoot[MAX_PATH] = { '\0' };
	char relativePath[] = { '\\', 'T', 'e', 'm', 'p', '\\', 'C', 'h', 'e', 'a', 't', ' ', 'E', 'n',
		'g', 'i', 'n', 'e', '\0' };

	const MODULE_ID SHELL32_MODULE_ID = HashString("Shell32.dll").GetHash();
	const FUNCTION_ID SHGETFOLDERPATHA_FUNCTION_ID = HashString("SHGetFolderPathA").GetHash();
	const SHGetFolderPathAFn shGetFolderPathA = (SHGetFolderPathAFn)GetFunctionAddressByHash(
		SHELL32_MODULE_ID, SHGETFOLDERPATHA_FUNCTION_ID);

	if (!SUCCEEDED(shGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataRoot)))
		return false;

	outPath = appDataRoot;
	outPath += relativePath;
	return true;
}

bool CEMetadataChecker::IsUsingCustomDirectory(DWORD value) const
{
	return value != 0;
}

bool CEMetadataChecker::CheckAddressFiles() const
{
	for (const atString& file : m_files)
	{
		if (!CheckAddressFile(file))
			return false;
	}

	return true;
}

This might only capture players who are using Cheat Engine in a vain attempt to cheat and get banned.

This is the include file used by the cheat detection code.

src\dev_ng\game\security\plugins\cedetectionplugin.h
#ifndef SECURITY_CEDETECTIONPLUGIN_H
#define SECURITY_CEDETECTIONPLUGIN_H

#include "security/ragesecengine.h"
#include "system/xtl.h"

#if USE_RAGESEC
#if RAGE_SEC_TASK_CE_DETECTION

// TODO: Use the window name finder to gather a list of PIDs that can be Cheat Engine
// Use NtQuerySystemInformation to check if those PIDs have a handle to the game.

#define CE_DETECTION_ENABLE_METADATA_CHECKER	1
#define CE_DETECTION_ENABLE_PROCESS_CHECKER		0
#define CE_DETECTION_ENABLE_WINDOW_NAME_FINDER	0

#if CE_DETECTION_ENABLE_WINDOW_NAME_FINDER
class WindowNameFinder
{
public:
	WindowNameFinder(const char* name) : m_name(name) {};
	~WindowNameFinder() {};

	bool Find();

private:
	static BOOL CALLBACK ProcessWindow(HWND hwnd, LPARAM lParam);

	const char* m_name;
};
#endif //CE_DETECTION_ENABLE_WINDOW_NAME_FINDER

#if CE_DETECTION_ENABLE_METADATA_CHECKER
class CEMetadataChecker
{
public:
	CEMetadataChecker() {};
	~CEMetadataChecker() {};

	bool Check();

	__forceinline static u64 GetSystemTime();
	static void PopulateImageBoundaries();

private:
	atString GetCheatEngineDataPath() const;
	atString MakeChildPath(const char* basePath, const char* sub) const;

	__forceinline bool IsAddressFile(const atString& name) const;
	void RetrieveAddressFilePaths();
	void RetrieveSubdirectories(const atString& parent, atVector<atString>& directories);
	void RetrieveFiles(const atString& directory);

	__forceinline bool CloseFileHandle(const fiDevice& device, const fiHandle& handle) const;
	__forceinline bool HasDirectoryAttribute(const fiFindData& findData) const;
	__forceinline bool IsInvalidFileHandle(const fiHandle& handle) const;
	__forceinline bool IsCurrentDirectory(const char* path) const;
	__forceinline bool IsParentDirectory(const char* path) const;

	bool GetSoftwareKey(HKEY& outKey) const;
	bool GetUsingTempDirectory(const HKEY& inKey, DWORD& outValue) const;
	bool GetCustomDataDirectory(const HKEY& inKey, atString& outValue) const;
	bool GetDefaultDataDirectory(atString& outPath) const;
	__forceinline bool IsUsingCustomDirectory(DWORD value) const;

	__forceinline bool CheckAddressFiles() const;
	__forceinline bool CheckAddressFile(const atString& path) const;
	__forceinline bool IsInScriptGlobalsRegion(const u64& address) const;
	__forceinline bool IsInGameImageRegion(const u64& address) const;

	static const u32 ADDRESS_FILE_START_OFFSET = 7;
	static const u64 ADDRESS_FILE_MAX_BUFFER = 4096;

	atVector<atString> m_files;
};
#endif //CE_DETECTION_ENABLE_METADATA_CHECKER

#if CE_DETECTION_ENABLE_PROCESS_CHECKER
class CEProcessChecker
{
public:
	CEProcessChecker() {}
	~CEProcessChecker() {}

	bool Check();

private:
	void CheckProcesses();

	bool HasHandleToCurrentProcess(const u32 pid);
	void GetFileNameForModule(HMODULE hMod, HANDLE hProcess, atString& outName);
};
#endif //CE_DETECTION_ENABLE_PROCESS_CHECKER

bool CEDetectionPlugin_Create();
bool CEDetectionPlugin_Configure();
bool CEDetectionPlugin_Destroy();
bool CEDetectionPlugin_Work();

bool CEDetectionPlugin_OnSuccess();
bool CEDetectionPlugin_OnCancel();
bool CEDetectionPlugin_OnFailure();

bool CEDetectionPlugin_Init();

#endif //RAGE_SEC_TASK_CE_DETECTION
#endif //USE_RAGESEC

#endif //!SECURITY_CHEATENGINEDETECTIONPLUGIN_H

This is just looking for strings instead of detecting memory manipulation, looking for certain memory addresses and using them to manipulate the game can easily allow cheating. Using a memory editor is a good way. This is commonly used, if the game stores data client-side and then it is written back to the server, it could be changed with a memory editor. Apparently Arma reforger stores data client-side, like the position of the player.

In modern multiplayer games, the architecture typically exists on a spectrum between purely server-sided and purely client-sided data access. There’s rarely a complete black-and-white answer. Here’s a breakdown of the two ends and the potential for exploitation:

Server-Sided Data:

  • Concept: All important game states and logic reside on the server. Clients receive updates and render the results.
  • Pros:
    • Security: Cheating is much harder as players can’t manipulate critical data directly.
    • Fairness: Everyone experiences the same game state, reducing inconsistencies and lag.
  • Cons:
    • Latency: Increased reliance on network communication can lead to input lag and delayed responses.
    • Server Costs: Server hardware and bandwidth requirements can be high.

Exploitation: While cheating is harder here, it’s not impossible. Techniques include:

  • Network manipulation: Players might try to alter sent or received data packets to gain advantages (e.g., modifying damage values).
  • Memory hacking: In less-secured servers, players might exploit vulnerabilities to access server memory and extract sensitive information.
  • Reverse engineering: Understanding the server-client communication protocol could allow players to build tools that manipulate game logic.

Client-Sided Data:

  • Concept: Some game logic and data reside on the client, enabling faster responses and rendering.
  • Pros:
    • Low Latency: Players experience smoother, more responsive gameplay with less input lag.
    • Reduced Server Load: Less reliance on the server can be cost-effective and improve scalability.
  • Cons:
    • Security: Clients have more opportunities to exploit bugs and manipulate gameplay for unfair advantages.
    • Inconsistent gameplay: Player-client discrepancies can lead to visual glitches and unfairness.

Exploitation: Client-sided data opens many doors for cheating:

  • Aimbots: Scripts can analyze client-sided game data to automatically aim at opponents.
  • Wallhacks: Exploiting rendering bugs or manipulating data can reveal hidden enemies or objects.
  • Speed hacks: Modifying client-side movement logic can grant players unfair speed advantages.

Modern Hybrid Approach:

Most modern games use a hybrid approach, balancing server-side authority with client-side prediction for responsiveness. Clients handle visual effects and animations, while the server maintains the authoritative game state and validates client actions. This minimizes cheating while maintaining smooth gameplay.


Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.