Game Development Community

Anti-Cheating

by Trevor Barnett · in Game Design and Creative Issues · 04/26/2001 (3:02 pm) · 9 replies

OK so if I CRC each file in a PAK file, combining that with the CRC of the PAK file itself, I could create some really hard to duplicate CRC values. Now, if I CRC the client in order to prevent a player from modding his client, that still doesn't prevent him from chaning that CRC function. What is a way to prevent such changes? I've been thinking of ways to use the CRC values of the program in order to do some necissary functions (like 3DS MAX and its dongle code, where even if all dongle code is removed the models still degenerate over time). Does anyone have some thoughts on how to incorporate these values so that they are necissary to the client? Or any other anti-cheating strategies? For the sake of keeping this topic limited, I'm really just interested in information about preventing the clients from editing the client side resources and client program itself.

#1
04/26/2001 (7:43 pm)
Trevor,

First, some background. I'm a long time hacker from the Mac community. I worked with everything from enterprise installations to shareware. It was just a hobby to take up some idle time and learn something along the way.

There was never a program I could not hack. Not once in over 10 years.

There were certainly some difficult ones though. I would caution you to avoid wasting your time trying to implement a hacker-proof scheme. However, if one hacker can do it, he can open it up for anyone to play with. Thus my advice, truly, is to put some CRCs in and call it a night.

That said...

I've designed some systems which are pretty tough to get around. Generally you need to do some trickery: Avoid making a call to a function which does all your copy protection. Avoid calling a 'gotcha' function anywhere near the code which does the CRC. Avoid using a variable or set of variables which keeps the state of the validation. Avoid global variables containing the validation states at all cost. Passing messages through windows, or using interrupt-driven timers are a good way (tough to trace back).

Don't read the EXE file. It's easy to spot. Read from memory (although, it's not portable).

CRCs are the hardest to find, they take up only 15-20 assembly instructions and can find single bit changes.

Don't call just one CRC. In fact, never even call the CRC as a function, always inline it and call it often.

A good method for setting validation variables is to use funky byte-alignments. Store a byte, but read a word. Shift the data around, then read out one byte seemingly at random. Never use the validation variable in a branch statement. Instead, use it as an index to a function pointer array. All the other entries go to dummy code, while one goes to the 'valid' code.

Self-modifying code and cache-tricks were definitely the best, but they're right out the window in the PC environment - too many different chips.

When it comes down to the wire, I'd just CRC the thing during install, and store that value in the registry. 99% of users would never figure it out.
--Bryan
#2
04/27/2001 (10:50 am)
OK, interesting. Yeah I have some experience as a cracker, using Soft-Ice and stuff, but I sucked at it. Anyway there is very little literature online about such things. Is there any example code concerning the random value reads, shifting stuff around and making it look random? The registry trick would work for most cases, but if the file is upgraded, id have to change the registry. And on the PC a common trick is to monitor registry traffic at install, to see what the program stores there. It only takes one, and Id like to make it as hard as possible for that one to succeed.
#3
04/27/2001 (12:51 pm)
// Trevor,
// Ok, here's a simple example...
// (view this in a code editor, it'll make your life easier).

struct bytes
{
char a;
char b;
char c;
char d;
}

inline DoCRC( char *someData, long *putCRCHere )
{
// Do the CRC
for ()
CRC = circular_shift( CRC ^ (*someData), 3 ); // Whatever..
// Save the CRC in the right place.
*putCRCHere = CRC;
}

void ValidateOne(long);
void ValidateTwo(long);
void ValidateThree(long);
void ValidateFour(long CRC)
{
// All these functions should do essentially the same thing, but slightly differently.
CRC ^= 0x3478322A; // Mangle the CRC differently in each one..
SendToServer( VALIDATION_CHECK, CRC );
}

void SomeCommonFunction()
{
long CRCOne, CRCTwo;
void (*func)()[4] = { ValidateOne, ValidateTwo, ValidateThree, ValidateFour };
DoCRC( &theExe, &CRCOne );
CRCOne <<= 4;
// Do some stuff..
// Call a function, pass it &CRCTwo. It calls DoCRC( &theExe, &CRCTwo );
// Do some more stuff..
CRCTwo >>= 12;

func[(((bytes *)&CRCOne).c + ((bytes *)&CRCTwo).b)%4]( CRCTwo + CRCOne );
}

/*
Setup:
- Server sends down a packet (periodically), containing a 'challenge' and a random number.
- The client builds a function table (randomly), based on the seed value from the server.
(this is not shown in the code, and the function table should be on the order of 32+ functions)
- During some common loop, a CRC is performed on a part of the system or data.
What is CRCed should be based on the seed from the server. Never the same thing twice.
- The data should be CRCed numerous times in completely different parts of the code.
Here I show two. Never call the CRC as a function, it should be inlined (replicated everwhere it's used).
- After some time, gather up the CRCs, shift them around a bit to make it more confusing.
Then, call an index from the function table. Notice it will always call a function, regardless of
the state of success. It always does the same thing.
- The server does the same CRCs & mangling, it knows what the client should return.
If the server detects that the validation failed, let them play for some random amount of time >5 min,
then disconnect with a timeout error (or other generic error).

--Bryan
*/
#4
04/30/2001 (1:42 am)
I agree with Bryan here - it is technically impossible to prevent a game mechanism from being cracked.

There is a good article on this issue which was up on Gamasutra.

A good technique for cheat prevention is to let the server police the clients behaviour. However, this still cannot catch all cheats.

Good luck,

Doug EnkiSoftware Limited
#5
04/30/2001 (8:38 am)
I know the article, I read it. My goal is to let the server police the clients, but I stil want to make it really hard for the client to be modified.
#6
05/03/2001 (4:31 pm)
i would think that spending valuable development time on excessive client-side protection is a waste.

If you can quickly and easily detect that the client has been modified from the server (via a server-side set of controls/limits), then this would be enough.

what do you care, beyond the fact that 'yes this client has been modified'. If this is true, kick the client with an appropriate warning and be done with it.

all that development time that you spend on authentication is time that isn't spent making your game the best it can be.
#7
05/03/2001 (4:40 pm)
That's what we were discussing... CRC the client and make sure it hasn't been edited.... Same with all other resources used by the game.
#8
05/03/2001 (5:28 pm)
Why not send the client some random data to the client to be included the CRC calculation?
#9
05/05/2001 (8:46 am)
I have posted the link to the anti-cheating article in the resources, as How to Hurt the Hackers: The Scoop on Internet Cheating and How You Can Combat It .

This is probably must reading for anyone who's interested in this discussion, as it sets the basic ideas.

One thing you might want to consider if you are making a game as a small developer is this: If you game doesn't get made then no-one is going to cheat. Also, if your game isn't popular, the chance of someone cracking the code to cheat is fairly low. Thus, my own advice (which I practice) is to have some carefull thoughts about anti-cheating, design and code the game with this in mind, but don't spend too long on actually implementing anti-cheating code. Since the game will be an online game, if cheating becomes a problem you can then tackle the issue by releasing a patch.

Which brings me to one way to try to 'cure' cheating: regular small patches which use randomised code and network protocols. Whilst possible, this would require significant tool building. A hacker might be able to create a program which analyses the code and finds the relevant sections to 'cheat', so the code would have to be very significantly altered upon each patch. Even then, since the CPU must be able to execute the instructions to produce the correct output, significiant CPU cycles would have to be wasted to cover your tracks.

My conclusions? - Don't worry, be happy.Oh - and make the game!

Doug EnkiSoftware Limited