Keep the CheateEngine out of your Flash Online Games

Recently, my colleague sent me a few YouTube links, showing how easy popular Flash Games (I won’t tell you any names) can be manipulated. If you don’t know what the CheatEngine is and/or how it works,
you should probably read this post: http://forum.cheatengine.org/viewtopic.php?t=18163. The core functionality of what is does is simple and very effective: It’s an extended memory modify/search tool.

Let’s say you display the user’s experience(XP) in your HUD as an uint. The potential attacker might note this number and put it into the CheatEngine to search the application’s memory for this value. He might get a huge number of
memory addresses returned, might be 100 just as an example, so he needs to filter the particular address. To filter the wanted address, the attacker just needs to get that number changed by the game itself – let’s say by shooting an enemy, so the XP will increase.

He’ll notice one or more addresses have changed. Only the changed values may belong to our XP, so he’ll have a good chance to select the right memory address that stores the XP.

To cheat more XP, all he needs to do, is to change the value at that memory address – that’s it! With the next update of the HUD (shooting another enemy), the new value will be displayed in the HUD, because most of the time developers will use objPlayer.xp += value;. The += first gets the value from the memory, puts it onto the stack, then adds the value behind the operator and stores the result back at that same memory address.

As long as you don’t re-check this value on a server-side, this is a large security hole. But even if you do, it’s not nice to have your HUD displaying 100 mio XP, where 1 mio is the overall maximum value. It’s not good for your game’s image.

Now you might ask yourself “I use uints and Numbers, how can I prevent manipulation?” The answer is “Don’t store the original values”.

  1. The first step should be to use some kind of cryptography, like multiplying the values when writing and dividing them when reading.
  2. The second step could be to use different memory addresses on each write phase.
  3. The third step might be to change the cryptography mechanism on each write phase.
  4. A fourth step could be storing the values twice, once for backup reasons with different crypto to the original values.
  5. Finally, if you want to track which values the attacker tried to change, you can add a sort of silent report to your server.

How does this work? Well, I’d like to provide some basic classes for this.
In the code below, you’ll see some errors, caused by the code parser of my blog (should always read “Vector.<uint>”).

First we’ve got our example Player class:

package my.game
{
	import my.game.cryptovars.CryptoUInt;
 
	public class Player
	{
		private var uintXP	:CryptoUInt;
 
		public function Player():void
		{
			this.uintXP = new CryptoUInt;
		}
 
		public function increaseXP(uintValue:uint):void
		{
			this.uintXP.value += uintValue;
		}
 
		public function traceXP():void
		{
			trace('XP: '+ this.uintXP.value);
		}
	}
}

Second, we need a basic cryptographic class that can be extended by the different types.

package my.game.cryptovars
{
	public class CryptoVar 
	{
		protected	var	uintCryptShiftBackup		:uint,
					uintCryptShiftProperty		:uint,
					uintPointerBackup		:uint,
					uintPointerProperty		:uint,
					uintVectorLengthBackup		:uint,
					uintVectorLengthProperty	:uint;
 
		public function CryptoVar():void
		{
			this.uintCryptShiftBackup	= Math.max(1, Math.random() * 8);
			this.uintCryptShiftProperty	= Math.max(1, Math.random() * 8);
 
			this.uintVectorLengthBackup	= Math.max(4, Math.random() * 32);
			this.uintVectorLengthProperty	= Math.max(4, Math.random() * 32);
		}
	}
}

Third, we extend the CryptoVar class and implement the algorithm for uints.

package my.game.cryptoVars
{
	public class CryptoUInt extends CryptoVar
	{
		private	var	vecSlotsBackup		:Vector.<uint>	,
				vecSlotsProperty	:Vector.</uint><uint>	;
 
		public function CryptoUInt(uintValue:uint = 0):void
		{
			super();
 
			this.vecSlotsBackup	= new Vector.</uint><uint>(super.uintVectorLengthBackup	, true);
			this.vecSlotsProperty	= new Vector.</uint><uint>(super.uintVectorLengthProperty	, true);
 
			this.value		= uintValue;
		}
 
// -- PUBLIC METHODS -- //
		public function destruct():void
		{
			var	i	:int,
				intLen	:int;
 
			i	= -1;
			intLen	= this.vecSlotsBackup.length;
			while(++i !== intLen)
			{
				this.vecSlotsBackup[i] = 0;
			}
 
			i	= -1;
			intLen	= this.vecSlotsProperty.length;
			while(++i !== intLen)
			{
				this.vecSlotsProperty[i] = 0;
			}
 
			super.uintCryptShiftBackup	= 0;
			super.uintCryptShiftProperty	= 0;
			super.uintPointerBackup		= 0;
			super.uintPointerProperty	= 0;
			super.uintVectorLengthBackup	= 0;
			super.uintVectorLengthProperty	= 0;
 
			this.vecSlotsBackup		= null;
			this.vecSlotsProperty		= null;
		}
 
		public function reportIntrusion(uintType:uint):void
		{
			trace('I N T R U S I O N   D E T E C T E D  | Backup ['+ this.vecSlotsBackup[super.uintPointerBackup] +']{Shift: '+ super.uintCryptShiftBackup +'} => '+ (this.vecSlotsBackup[super.uintPointerBackup] >>> super.uintCryptShiftBackup) +' |  Value ['+ this.vecSlotsProperty[super.uintPointerProperty] +']{Shift: '+ super.uintCryptShiftProperty +'} => '+ (this.vecSlotsProperty[super.uintPointerProperty] >>> super.uintCryptShiftProperty));
		}
 
// -- GETTERS AND SETTERS -- //
		public function get value():uint
		{
			var	uintBackup	:uint = this.vecSlotsBackup[super.uintPointerBackup]		>>> super.uintCryptShiftBackup		,
				uintValue	:uint = this.vecSlotsProperty[super.uintPointerProperty]	>>> super.uintCryptShiftProperty	;
 
			trace
			(
				'CryptoUInt::value[get]'			 		+
				' deciphered: '							+
				this.vecSlotsProperty[super.uintPointerProperty].toString()	+
				' deciphered(Backup): '						+
				this.vecSlotsBackup[super.uintPointerBackup].toString()
			);
 
			if(uintValue !== uintBackup)
			{
				this.reportIntrusion(super.uintType);
 
				//restore value from backup and write both with new positions
				super.uintPointerBackup					= Math.random() * super.uintVectorLengthBackup;
				this.vecSlotsBackup[super.uintPointerBackup]		= uintBackup < < super.uintCryptShiftBackup;
 
				super.uintPointerProperty				= Math.random() * super.uintVectorLengthProperty;
				this.vecSlotsProperty[super.uintPointerProperty]	= uintBackup << super.uintCryptShiftProperty;
 
				uintValue = uintBackup;
			}
 
			return uintValue;
		}
 
		public function set value(uintValue:uint):void
		{
			super.uintCryptShiftBackup				= Math.max(1, Math.random() * 8);
			super.uintCryptShiftProperty				= Math.max(1, Math.random() * 8);
 
			super.uintPointerBackup					= Math.random() * super.uintVectorLengthBackup;
			this.vecSlotsBackup[super.uintPointerBackup]		= uintValue << super.uintCryptShiftBackup;
 
			super.uintPointerProperty				= Math.random() * super.uintVectorLengthProperty;
			this.vecSlotsProperty[super.uintPointerProperty]	= uintValue << super.uintCryptShiftProperty;
 
			trace
			(
				'CryptoUInt::value[set] original: ' 				+
				uintValue.toString()						+
				' enciphered: '							+
				this.vecSlotsProperty[super.uintPointerProperty].toString()	+
				' enciphered(Backup): ' +
				this.vecSlotsBackup[super.uintPointerBackup].toString()
			);
		}
	}
}

You can apply this technique to all native types, including int, uint, Number, String and Boolean.
Please note that the limits of the native types change when using this technique, because values are shifted around, so this can cause some unexpected and strange behaviour. You could, for example, limit the shifting value.

Comments (0)

› No comments yet.

Leave a Reply

Allowed Tags - You may use these HTML tags and attributes in your comment.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Pingbacks (0)

› No pingbacks yet.