20 Jul 2012 @ 9:13 PM 

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.

  1. 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?
  2. 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.
  3. 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.
  4. 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 class to which said value is associated will decode it properly.

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 and not have to change anything else.

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 , then define operator methods that can cast a T to an Obfuscated and vice-versa without too many problems. problems start to arise as you realize that the original code is using operators and the implicit casts are not being performed (because there is no reason for the compiler to think that casting the two values from Obfuscated to the type T will make the operation valid). So you define operator overloads. The typical result in C# 3 was rather messy. Since there was no way to define a constraint that said to only allow arithmetic operators, nor is there an interface you could cast to to check if the Type T for which your generic instance was constructed supports arithmetic operators, you were left in the cold. For many types you could use the built-in operator methods themselves, op_Addition, op_Subtraction, etcetera. What you would end up with is a large else-if ladder that checked for all of the primitive types, with an else block that used reflection to call the appropriate operator method that way; and failing that throwing an InvalidOperationException.

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. public static byte []  TypeValueToBytes<P>(P valueget)
  3. {
  4.     if (typeof(P).IsValueType)
  5.     {
  6.         //convert the given to bytes.
  7.         int size = Marshal.SizeOf(valueget);
  8.         byte []  arr = new byte [size] ;
  9.         IntPtr ptr = Marshal.AllocHGlobal(size);
  10.         Marshal.StructureToPtr(valueget, ptr, true);
  11.         Marshal.Copy(ptr, arr, 0, size);
  12.         Marshal.FreeHGlobal(ptr);
  13.         return arr;
  14.     }
  15.     else //reference type
  16.     {
  17.  
  18.         //otherwise, serialize it to a memorystream and return the stream contents.
  19.         try
  20.         {
  21.             BinaryFormatter bf = new BinaryFormatter();
  22.             using (MemoryStream ms = new MemoryStream())
  23.             {
  24.                 bf.Serialize(ms, valueget);
  25.  
  26.  
  27.                 ms.Seek(0, SeekOrigin.Begin);
  28.                 byte []  retrievebuffer = new byte [ms.Length] ;
  29.                 ms.Read(retrievebuffer, 0, (int)ms.Length);
  30.                 return retrievebuffer;
  31.             }
  32.  
  33.         }
  34.         catch (Exception sex)
  35.         {
  36.             throw new ArgumentException("Failed to create Byte stream from reference type " + typeof(P).Name, sex);
  37.  
  38.  
  39.         }
  40.  
  41.     }
  42.    
  43. }
  44.  

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. public  static P TypeBytesToValue<P>(byte []  contents)
  3. {
  4.     //convert the given bytes to the object.
  5.     //assume presence of default constructor…
  6.    
  7.     Type usetype = typeof(P);
  8.     if (usetype.IsValueType)
  9.     {
  10.  
  11.         P newvalue = default(P);
  12.  
  13.         int size = Marshal.SizeOf(newvalue);
  14.         IntPtr ptr = Marshal.AllocHGlobal(size);
  15.         Marshal.Copy(contents, 0, ptr, size);
  16.         newvalue = (P)Marshal.PtrToStructure(ptr, usetype);
  17.         Marshal.FreeHGlobal(ptr);
  18.         return newvalue;
  19.     }
  20.     else
  21.     {
  22.         //otherwise, write the bytes to a memory stream and deserialize to an object.
  23.         try
  24.         {
  25.             BinaryFormatter bf = new BinaryFormatter();
  26.             using (MemoryStream ms = new MemoryStream())
  27.             {
  28.  
  29.                 ms.Write(contents, 0, contents.Length);
  30.                 ms.Seek(0, SeekOrigin.Begin);
  31.                 return (P)bf.Deserialize(ms);
  32.  
  33.  
  34.             }
  35.         }
  36.         catch (Exception exx)
  37.         {
  38.             throw new ArgumentException("Failed to retrieve reference value of type " + typeof(P).Name, exx);
  39.  
  40.  
  41.         }
  42.  
  43.     }
  44.  
  45.    
  46.  
  47. }
  48.  

by replacing a variable of a given type T with an Obfuscated , you get almost completely transparent obfuscation of the in-memory representation of that type. Assignments result in the generation of a new random number, encrypt the passed value, and store the encrypted value, and retrieving the value or an implicit cast to the type will deobfuscate the value and return that.

The Full source to my test project which contains and uses this class is attached:
Memory Obfuscation Class

718 total views, 4 views today

Posted By: BC_Programming
Last Edit: 20 Jul 2012 @ 09:13 PM

EmailPermalinkComments (0)
Tags
Tags: , , ,
Categories: .NET, C#, C/C++, Programming
 03 Jul 2012 @ 6:32 PM 

What is a Pointer?

To understand a pointer, one must better understand variables. In a statically typed language such as C, a variable has three properties- it has a type, it has a name, and it has a value.

  1.  
  2. int variable=400;
  3.  

In the above example, int is the type of this variable, variable is the name, and 400 is it’s value. A Pointer is essentially a variable that has a type and a name, but get’s it’s value from elsewhere. For example, in the following C program:

  1.  
  2. #include <stdio.h>
  3. int main(int argc,char** args)
  4. {
  5.     int a =5;
  6.     int* b = &a;
  7.     int* c = b;
  8.     printf("Initial Values:a=%d,b=%d,c=%d\n",a,*b,*c);
  9.    
  10.     //now, what if we change a?
  11.     a=12;
  12.     printf("Initial Values:a=%d,b=%d,c=%d\n",a,*b,*c);
  13.    
  14.    
  15.     return 0;
  16.  
  17. }
  18.  

a has a type, name, and a value. b and c, however, are declared as pointers. This is done by placing a asterisk between the type and the variable name. For the most part, a pointer is just a memory pointer, usually a long integer. in this example we set b to point to the address of a, and then set c to be the same as b. When we change a, the “values” of the other variables change to.

Since pointers are fundamentally integers of some description, all operations assume you are dealing with the pointer value (as in, the integer pointing into memory) as opposed to the value stored at that location. In order to get the value stored at the location a pointer points, you need to dereference the pointer. In C, this is accomplished by prefixing the variable name with a asterisk, as shown in the above printf()’s for b and c. dereferencing a pointer returns a non-pointer value of the type of that pointer, in this case, while b is a int*, *b is a int. Without the dereference, printf() will print out the numeric value of the pointer, which is not desired.

Why are Pointers important?

Pointers are important simply because they are used for a vast number of implementations of algorithms. Pointers form the basis of References, which are used in other languages like C# and Java. Pointers are essentially what a number of algorithms are built on; Sorting algorithms deal with pointers within a larger structure, Linked Lists deal obviously with a set of elements linked via pointers, and so on.

The Dangers

The Dangers of pointers are pretty easy to understand. In order to dereference a pointer, the memory it points at has to be valid. The most common problem stemming from this is dereferencing a NULL Pointer. (NULL being 0). in C and C++, doing this will either crash the program (without an error message, unless special care is taken), or cause undefined results. C++ has a number of library classes and templates (such as auto_ptr and smart pointers) designed to make these problems easier to identify by using C++ capabilities such as operator overloading. C#, Java, and other higher level languages are of course not exempt from the problems with pointers, because Pointers are references and both are accosted by the same set of problems. These come about in the form of NullReferenceExceptions. These managed languages do mitigate some common problems such as creating a pointer but making it point to the wrong location, or faulty pointer arithmetic, and so forth, by making those unnecessary (C# makes it possible using unsafe{} code blocks, though). The Core capability of Pointers is aliasing- that is, being able to refer to one thing in multiple locations by different names. in C, if you pass a pointer to a function, that function can change the contents of what pointer is pointing at, but it cannot change where the pointer points.

Function Pointers

Before we talk about Function pointers , one needs to understand Functions themselves. a function is in a programming sense very much the same as a function in the scientific sense; it takes one or more inputs and returns a result. For example, in C:

  1.  
  2. #include <stdio.h>
  3. int doubler(int argument);
  4. int main(int argc,char** args)
  5. {
  6.     printf("%d",doubler(16));    
  7.     return 0;
  8. }
  9. int doubler(int argument)
  10. {
  11. return argument*2;
  12. }
  13.  

This C code actually does a lot “under the covers” within the assembly:

  1.  
  2.     .file   "testc.c"
  3.     .def    ___main;    .scl    2;  .type   32; .endef
  4.     .text
  5. .globl _main
  6.     .def    _main;  .scl    2;  .type   32; .endef
  7. _main:
  8.     pushl   %ebp
  9.     movl    %esp, %ebp
  10.     andl    $-16, %esp
  11.     subl    $32, %esp
  12.     call    ___main
  13.     movl    $16, (%esp)
  14.     call    _doubler
  15.     movl    %eax, 28(%esp)
  16.     movl    $0, %eax
  17.     leave
  18.     ret
  19. .globl _doubler
  20.     .def    _doubler;   .scl    2;  .type   32; .endef
  21. _doubler:
  22.     pushl   %ebp
  23.     movl    %esp, %ebp
  24.     movl    8(%ebp), %eax
  25.     sall    %eax
  26.     leave
  27.     ret
  28.  

Functions in low-level Assembly language consist of three parts: a prolog, an epilog, and the body. the prolog does the task of “housekeeping” and setting up the stack frame for the function. The epilog tears it down. Without getting into to many details, sometimes these parts of code need to be done before calling the function, or from within the called function. Either way, it boils down to a Assembly “CALL” instruction. The CALL Instruction (again, without going to in depth because if I do that I’m bound to make numerous factual errors, assuming I haven’t already) essentially moves the instruction pointer (which indicates the current location of program execution) to a new location. It also saves the return location for the subsequent RET instruction that is usually the last instruction executed in a function. the CALL instruction takes one thing: a Pointer.

Of course, we don’t usually work in assembly, do we? So how does this work in higher level languages, such as C? C actually does the work for you in many respects. In the above program, for example, we didn’t have to know about epilog, prolog, the stack frame, or any of that sort of stuff. We simply defined functions and used them. A Function pointer is a pointer which points at a function, rather than a storage location. Because of the Prolog and Epilog code needed to handle parameters and return values and stack frames, the Function pointer type needs to include information about Function parameters. a Function Pointer is defined in C this way:

  1.  
  2. int (*ptFunction)(int,int) = NULL;
  3.  

This creates a function Pointer ptFunction which is made to point at a function accepting two int arguments and returning an int, and that Pointer is initialized to NULL. In order to use it, it needs to have a value, this is done by making the pointer actually point to something:

  1.  
  2. int somefunction(int arg1,int arg2)
  3. {
  4.    return arg1*arg2;
  5.  
  6. }
  7.  
  8. int main(int argc,char** args)
  9. {
  10.    int (*ptFunction)(int,int) = NULL;
  11.    ptFunction=&somefunction;
  12.    int result = ptFunction(12,13);
  13.  
  14.  
  15. }
  16.  

Obviously, the real power in Function Pointers comes from being able to change what they point at. Add to this you can have functions that return other functions, and you have the beginnings of Functional Programming style.

In my next entry on this subject, we will move into C# and explore what C# delegates add to this and how they differ from your standard Function Pointer.

506 total views, 4 views today

Posted By: BC_Programming
Last Edit: 04 Jul 2012 @ 06:57 AM

EmailPermalinkComments (0)
Tags

 Last 50 Posts
 Back
Change Theme...
  • Users » 856
  • Posts/Pages » 191
  • Comments » 67
Change Theme...
  • VoidVoid « Default
  • LifeLife
  • EarthEarth
  • WindWind
  • WaterWater
  • FireFire
  • LightLight

PP



    No Child Pages.

Windows optimization tips



    No Child Pages.