Gamers are an enterprisey bunch. You don’t give them enough X, so they go prodding around your game’s memory to try to give themselves more. Programs that have this purpose are called “Cheat engines”. Usually, how they work is a multi-step process.
- The obvious first step is for the cheater to decide what they want. Lives? More Gold? Maybe they want to up their players attack power?
- This is usually fairly easy, and you can usually tell the data type just by the amount of screen space reserved for a value. Though sometimes you just have to guess. Many cheat engines can search for values regardless of the actual data type, too.
- Now, the cheater looks for every value in the game’s memory that has the current value of the item they want to ‘hack’. The results will usually be collosal.
- In order to revise the search parameters, the gamer/cheater will now “change” that value somehow; by perhaps losing a life or getting a new one, getting more score, using a “attack upgrade” pill, or what-have-you. Armed with the new value, they can now tell the cheat engine program to show them all the values that were the old value before that have now taken on the new value. If they are lucky, this will be only one entry, and they can use the cheat engine and change it, and BAM they have the new value they hacked in.
The typical, and perhaps smartest way, to deal with this problem is to ignore it. A well architected game system will have any multiplayer values centralized on a server which means that a cheat engine will only mess around with a client of the game, and there are ways of checking for those hacked clients as well, (because they can give players a minor advantage). However it bears an interesting topic of discussion- how do you prevent this?
With some considerations, I have determined a method to do this- each time a value is assigned, generate a new random value, store that, and then only store a version of the original value that was encrypted. The idea being that the existence of the “actual” value- the value the cheater would be able to identify- is transient and only exists in limited scopes, making it nearly impossible to find the correct value to change, and even if they do find it, the method of encryption/hashing means they would need to know the corresponding algorithm and number in order to turn a new value they wanted and store it in memory so the Obfuscated
The trouble with this method lies in the fact that it is anything but transparent, and requires copius code changes all over the place.
Enter Generics & Templates
Generics and Templates are the obvious solution to this problem. With C++, this sort of class nearly writes itself, actually; since templates are a compile time feature. you could then replace values that are int with something like Obfuscated
Being that I work primarily in C#, however, this was the focus of my attentions. And it is not quite as simple, though thankfully the addition of dynamics to the language really helps make the code more concise for this sort of purpose.
Everything seems to work fine initially; you can create a generic class Obfuscated
Dynamics to the rescue, however. Thanks to C# 4.0’s addition of the dynamic keyword, which introduced late-bound variables, we can actually make the call relatively simple. we can perform operators on dynamic variables and the call will be resolved at run-time, and will be handled appropriately even for the primitive types.
With that out of the way, there was only one question left: the encoding. I decided on a simple interface that allowed to decode and encode a arbitrary set of bytes. I implemented a simple xor encryption and used that for my tests. The tricky portion is of course getting some arbitrary set of bytes representing a variable of your generic type; this isn’t something you can really force with constraints. You really need a Type T that is either a structure or a serializable value. For this purpose I created two general purpose generic methods:
First, the method to convert a given value of a Type P to a array of byte[]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public static byte[] TypeValueToBytes<P>(P valueget) { if (typeof(P).IsValueType) { //convert the given to bytes. int size = Marshal.SizeOf(valueget); byte[] arr = new byte[size]; IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(valueget, ptr, true); Marshal.Copy(ptr, arr, 0, size); Marshal.FreeHGlobal(ptr); return arr; } else //reference type { //otherwise, serialize it to a memorystream and return the stream contents. try { BinaryFormatter bf = new BinaryFormatter(); using (MemoryStream ms = new MemoryStream()) { bf.Serialize(ms, valueget); ms.Seek(0, SeekOrigin.Begin); byte[] retrievebuffer = new byte[ms.Length]; ms.Read(retrievebuffer, 0, (int)ms.Length); return retrievebuffer; } } catch (Exception sex) { throw new ArgumentException("Failed to create Byte stream from reference type " + typeof(P).Name, sex); } } } |
If it is a ValueType, we use the Marshaller to get the actual in-memory representation of the structure, copy it to a array of bytes of the appropriate size, and then return that. Otherwise, we try to save it to a MemoryStream using a BinaryFormatter, slurp out the contents of that memorystream, and return that array of bytes to the caller. The corresponding function that turns a given array back into the corresponding type is obviously just the reverse of this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
public static P TypeBytesToValue<P>(byte[] contents) { //convert the given bytes to the object. //assume presence of default constructor... Type usetype = typeof(P); if (usetype.IsValueType) { P newvalue = default(P); int size = Marshal.SizeOf(newvalue); IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.Copy(contents, 0, ptr, size); newvalue = (P)Marshal.PtrToStructure(ptr, usetype); Marshal.FreeHGlobal(ptr); return newvalue; } else { //otherwise, write the bytes to a memory stream and deserialize to an object. try { BinaryFormatter bf = new BinaryFormatter(); using (MemoryStream ms = new MemoryStream()) { ms.Write(contents, 0, contents.Length); ms.Seek(0, SeekOrigin.Begin); return (P)bf.Deserialize(ms); } } catch (Exception exx) { throw new ArgumentException("Failed to retrieve reference value of type " + typeof(P).Name, exx); } } } |
by replacing a variable of a given type T with an Obfuscated
The Full source to my test project which contains and uses this class is attached:
Memory Obfuscation Class
Have something to say about this post? Comment!