struct Obfuscated<T> - enjoy?

Grab your favourite IDE and tinker with the innards of game engines

struct Obfuscated<T> - enjoy?

Postby coder0xff on Thu Aug 05, 2010 5:56 am

Umm, so as some of you know, I've been working on my own from-scratch engine, and have been implementing lots of "general purpose" stuff. I just finished making a struct called Obfuscated<T> in C#. So say you have something like:

Code: Select all
Obfuscated<int> playerHealth = 100; //encrypt the value and store it in playerHealth
playerHealth = 98;                  //encrypt a new value and store it in playerHealth
playerHealth += 2;                  //C# is smart enough to do the int conversion here too.
int getHealth = playerHealth;       //decrypt the value and store it in a regular int
Console.WriteLine(playerHealth);    //decrypt the value and print it


enjoy?
Code: Select all
    ///<summary>
    ///Encapsulates the obfuscation of plain-old-data types within memory
    ///using XOR encryption.
    ///</summary>
    ///<remarks>
    ///The encryption is not intended for secure transmission, but merely
    ///for obfuscation of the value in computer memory, and fast
    ///performance. It is not a single, simple XOR however, and provides
    ///some non-linearity. The XOR mask varies with each execution of the
    ///program, therefore serialization and deserialization work with the
    ///unencrypted value. When compiled with DEBUG, the values aren't
    ///stored encrypted for debugging convenience. Even so, the
    ///computations are still performed so that performance is not
    ///misrepresented.
    ///</remarks>
    ///<example>This example initializes an obfuscated integer with a value
    ///of 5, overwrites with 3, and then reads the current value.</example>
    ///<code>
    ///class MyClass
    ///{
    ///  public static int Main()
    ///  {
    ///    Obfuscated&lt;int&gt; myInt = 5;
    ///    myInt = 3;
    ///    int readValue = myInt;
    ///  }
    ///}
    ///</code>
    ///<typeparam name="T">The type to encapsulate</typeparam>
    //pack 1 keeps pad bytes from appear after storage
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    [Serializable]
    public struct Obfuscated<T> : ISerializable where T : struct
    {
        //if LayoutKind.Explicit was allowed, we could actually have a
        //dummy be in the same place as storage which would allow us to
        //transform in place with a * instead of having to use a copy via
        //Marshal class. For now it's a bit slower but the benefit is that
        //Obfuscated<T> is the same size as T.
        private T storage;
        private static int structSize;
        private static byte[] xorValues;

        private Obfuscated(SerializationInfo info,
            StreamingContext context)
        {
            T temp;
            temp = (T)info.GetValue("v", typeof(T));
            this = temp;
        }

        void ISerializable.GetObjectData(SerializationInfo info,
            StreamingContext context)
        {
            T temp = this;
            info.AddValue("v", temp);
        }

        /// <summary>
        /// Make sure that the type is not a reference type, and recurse so
        /// that it contains no members that a reference type.
        /// </summary>
        /// <param name="t">The type to check</param>
        /// <param name="alreadyVerified">A list that will be filled with
        /// already checked types to avoid recursion</param>
        private static void VerifyTypeIsPlainOldData(Type t,
            List<Type> alreadyVerified)
        {
            if (!t.IsValueType) throw new InvalidOperationException(
                "Cannot encrypt " + t.FullName +
                " because it is not a value type.");
            alreadyVerified.Add(t);
            FieldInfo[] fields = t.GetFields(BindingFlags.Instance |
                BindingFlags.NonPublic | BindingFlags.Public);
            foreach (FieldInfo field in fields)
            {
                Type nextT = field.FieldType;
                if (!alreadyVerified.Contains(nextT))
                    VerifyTypeIsPlainOldData(nextT, alreadyVerified);
            }
        }

        private static void generateXORValues()
        {
            Random rnd = new Random();
            xorValues = new byte[structSize];
            for (int index = 0; index != structSize; index++)
            {
                //values 1 - 255 since we want to avoid zero
                xorValues[index] = (byte)(rnd.Next(255) + 1);
            }
        }

        /// <summary>
        /// One-time initialization and error checking.
        /// </summary>
        static Obfuscated()
        {
            Type encapsulatedType = typeof(T);
            structSize = Marshal.SizeOf(encapsulatedType);
            if (structSize == 0) throw new InvalidOperationException(
                "Cannot encrypt data of zero size.");
            VerifyTypeIsPlainOldData(encapsulatedType, new List<Type>());
            generateXORValues();
        }

        /// <summary>
        /// Apply the XOR transform to the data at the specified address.
        /// This transform is symmetrical, and therefore used by encrypt
        /// and decrypt.
        /// </summary>
        /// <param name="ptr"></param>
        private static unsafe void Xor(byte* ptr)
        {
            for (int index = 0; index != structSize; index++)
            {
                *(ptr + index) ^= xorValues[index];
            }
        }

        /// <summary>
        /// Apply an encryption step that makes values in lower order bytes
        /// affect higher order bytes (little-endian)
        /// </summary>
        /// <remarks>
        /// With only a regular XOR, an increment or decrement of an
        /// integer may result in similarly small changes and it still
        /// allows more advanced pattern matching. By making lower order
        /// bytes affect higher order ones we remove this predictability.
        /// We go from lower to higher addresses for little-endian
        /// </remarks>
        /// <param name="ptr"></param>
        private static unsafe void byteSteppingXorEncrypt(byte* ptr)
        {
            int structSizeMinus1 = structSize - 1;
            for (int index = 0; index != structSizeMinus1; index++)
                *(ptr + index + 1) ^= *(ptr + index);
        }

        private static unsafe void byteSteppingXorDecrypt(byte* ptr)
        {
            for (int index = structSize - 2; index != -1; index--)
                *(ptr + index + 1) ^= *(ptr + index);
        }

        private static unsafe void bitSteppingXorEncrypt(byte* ptr)
        {
            for (int index = 1; index != 8; index++)
                *ptr ^= (byte)((*ptr << 1) & (2 << index));
        }

        private static unsafe void bitSteppingXorDecrypt(byte* ptr)
        {
            for (int index = 7; index != 0; index--)
                *ptr ^= (byte)((*ptr << 1) & (2 << index));
        }

        public unsafe static implicit operator T(Obfuscated<T> value)
        {

            IntPtr buffer = Marshal.AllocHGlobal(structSize);
            //if we could get a pointer to value.storage we could avoid all
            //the Marshal stuff
            //but C# is stubborn and wont let us do &value, or
            //&value.storage
            Marshal.StructureToPtr(value.storage, buffer, false);
            byteSteppingXorDecrypt((byte*)buffer);
            bitSteppingXorDecrypt((byte*)buffer);
            Xor((byte*)buffer);
            T result = (T)Marshal.PtrToStructure(buffer, typeof(T));
            Marshal.FreeHGlobal(buffer);
#if DEBUG   //still do all the above so we don't misrepresent performance
            //but stored values actually aren't encrypted for debug builds
            //for convenience
            return value.storage;
#else
            return result;
#endif
        }

        public unsafe static implicit operator Obfuscated<T>(T value)
        {
            Obfuscated<T> result = new Obfuscated<T>();
            IntPtr buffer = Marshal.AllocHGlobal(structSize);
            Marshal.StructureToPtr(value, buffer, false);
            Xor((byte*)buffer); //first the simple XOR
            //make the first bit a bit more non-linear
            bitSteppingXorEncrypt((byte*)buffer);
            //and then all the other bytes
            byteSteppingXorEncrypt((byte*)buffer);
            result.storage = (T)Marshal.PtrToStructure(buffer, typeof(T));
            Marshal.FreeHGlobal(buffer);
#if DEBUG   //still do all the above so we don't misrepresent performance
            //but stored values actually aren't encrypted for debug builds
            //for convenience
            result.storage = value;
#endif
            return result;
        }

        public override string ToString()
        {
            //does a decrypt
            return ((T)this).ToString();
        }
    }


This is useful for hiding important game state information from hackers. Since the values aren't stored directly in memory, the hacker doesn't know what value to look for.

P.S. IVE EDITED SO MANY TIMES. Keep making improvements. It's looking very solid now though.
User avatar
coder0xff
Veteran
Veteran
 
Joined: Fri Jun 13, 2008 1:51 am

Re: struct Obfuscated<T> - enjoy?

Postby zombie@computer on Thu Aug 05, 2010 4:35 pm

lol?

i just use XNA for my engine.
When you are up to your neck in shit, keep your head up high
zombie@computer
Forum Goer Elite™
Forum Goer Elite™
 
Joined: Fri Dec 31, 2004 5:58 pm
Location: Lent, Netherlands

Re: struct Obfuscated<T> - enjoy?

Postby coder0xff on Thu Aug 05, 2010 4:39 pm

zombie@computer wrote:lol?

i just use XNA for my engine.


Yeah, I would typically not re-invent the wheel. I have my reasons though. I've got some big things planned for it. :-D I have to ask, does XNA already have a struct/class that does the same thing? Most of the things I've implemented so far, I wasn't able to find any existing C# implementations - or even .Net.
User avatar
coder0xff
Veteran
Veteran
 
Joined: Fri Jun 13, 2008 1:51 am

Re: struct Obfuscated<T> - enjoy?

Postby zombie@computer on Thu Aug 05, 2010 4:41 pm

coder0xff wrote:
zombie@computer wrote:lol?

i just use XNA for my engine.


Yeah, I would typically not re-invent the wheel. I have my reasons though. I've got some big things planned for it. :-D I have to ask, does XNA already have a struct/class that does the same thing? Most of the things I've implemented so far, I wasn't able to find any existing C# implementations - or even .Net.
no, but then again, i dont write multiplayer games in xna nor do i care about hackers and whatnot.
When you are up to your neck in shit, keep your head up high
zombie@computer
Forum Goer Elite™
Forum Goer Elite™
 
Joined: Fri Dec 31, 2004 5:58 pm
Location: Lent, Netherlands

Return to Programming

Who is online

Users browsing this forum: No registered users

cron