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 Vector
s of Number
s 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 Number
s. 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.
Comments
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
Submit a comment