SafeNumber and SafeInt: Protecting your SWF against memory hacking

I'm currently making a game at the minute so I've been looking a lot into protecting my SWF file and stopping people from hacking my game. I came across a video where Nate Beck and Aaron Boushley from PushButtonEngine fame discuss the different ways hackers can hack your game and cheat their way to glory. The video's just over an hour long, but it's a recommended watch if you're serious about security.

One of the things they talk about is people searching through your memory, finding sensitive values, then changing or freezing them at will. This can be the score, life, power, sizes, whatever; anything that gives them an advantage. They don't even need to know the name of it. They can simply search for values that have increased or decreased, and through a process of elimination, narrow down to the variable they're looking for. As the SWF format is openly documented, this is relatively easy to do.

What to do

In the video, they talk about implementing a method of obfustication (making it harder for hackers to find what they need) which they named SafeNumber and SafeInt. Wrapping the value in a class and using Math.random() to mask it's value means that it's hard for the hacker to pinpoint the value they're looking for. By also using Math.random() between a range of -1.0 to 1.0 (for example), it means that hackers can't simply just looking for values that are constantly increasing or decreasing.

The implementation

With the methodology in place, I set about creating SafeNumber and SafeInt so I could use them in my project. So without further embellishment for the sake of filling space, here's the SafeNumber class:

package com.divillysausages.ds.security 
{
	/**
	 * Holds a number value safely to stop it being sniffed out and changed. Only use this
	 * for important numbers as there is a cost to it. It's much slower than using a normal
	 * var (mainly because of the getter/setter access). There's also a small margin for 
	 * error because of the conversion
	 * @author Damian
	 */
	public class SafeNumber
	{
		
		/**************************************************************/
		
		{
			// static constructor
			SafeNumber._init();
		}
		
		/**************************************************************/
		
		private static var m_keys:Vector.<Number> 	= null;	// the list of security keys to use
		private static var m_invKeys:Vector.<Number>	= null;	// the list of inv keys (quicker to multiply than divide)
		private static var m_currIndex:int		= 0;	// the current index for our number
		
		/**************************************************************/
		
		// inits the SafeNumber class, filling up our arrays
		// NOTE: We can't prepend the vars with "SafeNumber" as it comes out as null 
		// (not created yet?)
		private static function _init():void
		{
			var max:int 	= 100;
			m_keys 		= new Vector.<Number>( max, true );
			m_invKeys	= new Vector.<Number>( max, true );
			
			// pre-generate random numbers so it's accessed quicker
			for ( var i:int = 0; i < max; i++ )
			{
				var num:Number 	= -100.0 + Math.random() * 200.0;
				m_keys[i]	= num;
				m_invKeys[i]	= 1.0 / num;
			}
		}
		
		/**************************************************************/
		
		private var m_value:Number 	= 0.0;	// our value
		private var m_index:int		= 0;	// the index of our key
		
		/**************************************************************/
		
		/**
		 * The value for this Number. When being set, a new key is used to save it, 
		 * and when being retrieved, the inverse key is used to decrypt it
		 */
		public function get value():Number { return this.m_value * SafeNumber.m_invKeys[this.m_index]; }
		public function set value( n:Number ):void
		{
			this.m_index = SafeNumber.m_currIndex++;
			if ( SafeNumber.m_currIndex >= SafeNumber.m_keys.length )
				SafeNumber.m_currIndex = 0;
			this.m_value = n * SafeNumber.m_keys[this.m_index];
		}
		
		/**************************************************************/
		
		/**
		 * Creates a new SafeNumber
		 * @param n	The initial value to set it at
		 */
		public function SafeNumber( n:Number = 0.0 )
		{
			this.value = n;
		}
		
	}

}

What's going on?

I'll go through and explain what's going on here. First things first, the static constructor:

{
	// static constructor
	SafeNumber._init();
}

I learned about this handy piece of code a while back through a random link. What it does, is execute what's in the brackets the first time your class is referenced. This can be really handy for initialising different vars. The only drawback seems to be that in the _init() function, you can't actually reference the class. Normally I'd reference static members like SafeNumber.m_keys.... If I try to do this within the static constructor it throws an error (perhaps because the class itself isn't loaded yet?).

The _init() function itself is pretty straightforward:

private static function _init():void
{
	var max:int 	= 100;
	m_keys 		= new Vector.<Number>( max, true );
	m_invKeys	= new Vector.<Number>( max, true );
	
	// pre-generate random numbers so it's accessed quicker
	for ( var i:int = 0; i < max; i++ )
	{
		var num:Number 	= -100.0 + Math.random() * 200.0;
		m_keys[i]	= num;
		m_invKeys[i]	= 1.0 / num;
	}
}

I create 2 fixed Vectors of Numbers between a range of -100.0 and 100.0. You can make this range whatever you want, but I'd recommend having an even mix of negative and positive values. Creating Vector arrays means that we can avoid a Math.random() call every time we set our number (which if you're using it for a time variable can be a lot). m_invKeys contains the inverse key (i.e. 1.0 / m_keys[i]). This is another speed tweak. It's much quicker to multiply than it is to divide, so by doing this we gain a bit more.

The variables for the class are pretty straightforward:

private var m_value:Number 	= 0.0;	// our value
private var m_index:int		= 0;	// the index of our key

m_value is our obfusticated value, while m_index is the Vector index of our key.

The getter and setter of the class do all the hard work:

public function get value():Number { return this.m_value * SafeNumber.m_invKeys[this.m_index]; }
public function set value( n:Number ):void
{
	this.m_index = SafeNumber.m_currIndex++;
	if ( SafeNumber.m_currIndex >= SafeNumber.m_keys.length )
		SafeNumber.m_currIndex = 0;
	this.m_value = n * SafeNumber.m_keys[this.m_index];
}

The getter simply returns our obfusticated value by the inverse key to obtain the original value. The setter gets the next index in line for the key Vector (we simply loop back when we're at the end) and obfusticates our value by mutliplying it with the key.

The SafeInt class

The SafeInt code is practically identical save for a few small changes in the getter and setter:

public function get value():int { return int( this.m_value * SafeInt.m_invKeys[this.m_index] + 0.5 ); } // +0.5 = equivalent of Math.round()
public function set value( i:int ):void
{
	this.m_index = SafeInt.m_currIndex++;
	if ( SafeInt.m_currIndex >= SafeInt.m_keys.length )
		SafeInt.m_currIndex = 0;
	this.m_value = i * SafeInt.m_keys[this.m_index];
}

Our m_value is still a Number to work properly with the multiplication and division of the random number, so we need to convert back to an int. We add 0.5 before casting it (this is the fastest way to do a Math.round()) as there's a small rounding error present when using Numbers. A value of 5.0 when obfusticated and deobfusticated may return as 5.000000001 or 4.999999999 depending on the key. If we cast 4.999999999 to an int, we get 4, so by adding 0.5 we ensure that it falls on the right number that we want.

Is this really the best way?

This method is only an obfustication. You can create your own methods of obfusticating the numbers if you wish. If you use the Mochi API, they include a MochiDigits class that uses bit shifting and Strings to obfusticate the Number. It's more secure but also much slower. It's all a balance between speed and encryption.

What to expect

Using the SafeNumber or SafeInt classes will have a negative impact on the performance of your game. The speed difference between using this class and a normal var is mainly down to the access through the getter and setter methods, and that said it's not too bad - just don't use it for every value in your game :)

With the SafeNumber class, there's also a small error margin due to the multiplication/inverse multiplication that was talked about just above. This is in the range of something like -0.000000001 - 0.000000001 so it shouldn't affect you too much. SafeInt is fine as it's cast before being returned.

Improvements

The SafeNumber and SafeInt classes can be extended if needed to include the ability to check a value against the value stored to make sure it hasn't changed in the meantime. For the SafeNumber, I'd compare against a range rather than a hard number (which is never really a good idea with floating point numbers).

Example

I've embedded a small SWF example showing the SafeNumber and SafeInt in action. Just click on the stage to increase the values. The value will be shown along with what it's actually stored as in memory. The stored values are rounded down to make them easier to see. Each time you click, you add 0.5 to the SafeNumber and 1 to the SafeInt. You can download the two classes at the bottom of the page.

AttachmentSize
SafeNumber.as2.64 KB
SafeInt.as2.58 KB

Share:

Comments

Nate Beck
Sun, 30/01/2011 - 11:25

Glad you liked the video from 360Flex. I like what you did with it, especially pre-generating the random numbers.

Keep up the great work!

Cheers,
Nate Beck