Menu ▾ ▴

LearnInjector

The Injector

The Injector consists of a class that is specialized in injecting/inserting both arbitrary code and data into a target process' memory space. This class has been created with the main purpose of making it easier to modify the behaviour of processes that are running in the user's machine, by injecting binary instructions (assembly opcodes) into the process' memory space and redirecting the flow of execution of the process' program to the injected code. This technique is also popularly known as "code cave". The Injector can calso create/inject variables (data) which can be used to easilly exchange values between RAMvader's host process and the target process. Injected variables can also be used in the injected code caves' logic.

The Injector class is the beast that unleashes the full power of the RAMvader library!

From now on, for the sake of simplicity, we will call the injected binary code as "code caves", and the variables allocated by the Injector as "injected variables".

What are "Memory Alteration Sets"?

Starting on RAMvader 1.2, the Injector can now group sets of modifications on the target process' memory space and enable or disable these modifications all at once. This has been done because there are many cases where you need to modify several points of a target process' memory space in order to achieve some desired effects on the target process' behaviour (e.g., when making a game trainer, sometimes you need to alter several instructions in several different places of the game's memory to "activate" a cheat/hack on it).

Let's use an example to make the power of the Memory Alterations Sets clearer. Pretend you are creating a game hack for infinite HP, and for enabling that hack you need to replace three instructions in three different places of the game's memory space by "NOP" instructions. You might create a group containing these three "replace-by-NOP alterations" and then you might enable and disable that "infinite HP" cheat whenever you want!

To enable the cheat, you just tell the Injector to enable the Memory Alteration Set that contains the three NOP instructions that you have created. To disable the cheat, you just tell the Injector to disable that same Memory Alteration Set: the Injector will then automatically recover the original bytes of the three instructions that it had previously replaced by NOP when you activated the cheat!

As you will see, you can instruct the Injector to do many kinds of alterations, such as replacing instructions by NOP, replacing instructions by CALL instructions, replacing instructions/values by a predefined sequence of bytes, replacing instructions by JUMP instructions, etc. The Injector knows how to do all of these alterations automatically, and also knows how to undo them and recover the target process' original bytes of memory whenever necessary!

Thus, the Memory Alteration Sets feature turns the Injector into a sort of powerful "cheat engine".

Briefing on the Injector class

You can define multiple code caves, and you can see each one of them as a new "function" that you are injecting into the target process' code. You can also define multiple variables to be injected into the target process' memory space to be used for several different things, as you would expect in any program. Thus, you must have a way to identify each code cave (function) and each variable that you have injected into the target process. The RAMvader library expects you to identify each of these different code caves and variables through enumerations in your code.

For Memory Alteration Sets this is not different: just like with each variable and each code cave that you need the Injector to inject into the target process' memory space, each set of alterations must also be identified by an enumerator.

As you will learn in the following paragraphs, you need to provide definitions (during runtime) which specify the binary code for the code caves, the values and types of the variables you are injecting, and the memory alteration sets. All of these definitions are optional, as you might choose not to use code caves, injection variables or memory alteration sets - these are three independent things, although you can mix them together if you want. You can provide the aforementioned definitions to an instantiated Injector object. Memory alteration sets are created by using the Injector methods, while code caves' and injection variables' definitions can be created independently.

The Injector class is a generic class which receives three parameters:

  • 1. The first parameter is the enumeration used to identify the memory alteration sets.
  • 2. The second one is the enumeration used to identify the code caves. This documentation will explain the process of building a CodeCaveDefinition during runtime, for the Injector to know the bytes that compose each code cave, so that it can inject the code caves into the target process' memory space.
  • 3. The third and last parameter is the enumeration used to identify the injection variables. This documentation will explain the process of building a VariableDefinition during runtime, for the Injector to know the type of variable and initial value that it needs to inject into the target process' memory space.

So you are required to pass these three types of enumerations for the Injector. Whenever you do not need one of these enumerations, you may safely declare an empty enumeration, but you may not omit the declaration of any of these enumerations. That is: if you do not want to use Memory Alteration Sets, just declare an empty enumeration with no identifiers for any memory alteration set; if you do not want to inject code caves into the target process' memory space, then just declare an empty enumerations containing no identifiers for code caves; and if you do not want to inject any variables, just declare an empty enumeration, containing no identifiers for variables.

It is up to you whether you use all the functionalities the Injector offers or just part of them! It is also up to you to define the way you mix these parts. E.g.: you can decide NOT to use the memory alteration sets, while you create code caves that have references to the addresses of injected variables which your program controls, for your program to dynamically alter the behaviour of the target process by setting the injected variables' values, for instance.

Instantiating an Injector

The Injector only works when associated with a RAMvaderTarget object which represents the target process. All reading and writing operations that the Injector performs are done by using its associated RAMvaderTarget instance. So, after the Injector is instantiated, you need to specify the RAMvaderTarget it is associated to.

The following code instantiates an Injector object whose Memory Alteration Sets' identifiers are defined in the code caves are defined in the EMyMemoryAlterationSets enumeration, code caves' identifiers are defined in the EMyCodeCaves enumeration, and injection variables' identifiers are defined in the EMyVariables enumeration. After the Injector is instantiated, we associate it to a previously instantiated and configured RAMvaderTarget object, which will be used to access the target process' memory space. This is done by calling the SetTargetProcess() method.

// Instantiates an Injector
var injectorObj = new Injector<EMyMemoryAlterationSets, EMyCodeCaves, EMyVariables>();

// Associate the Injector to a RAMvaderTarget
// (assume "myTarget" is a previously instantiated and configured RAMvaderTarget object)
injectorObj.SetTargetProcess( myTarget );

After we have an instantiated Injector, we can begin operating on it. We can define injection variables, code caves and Memory Alteration Sets on it. These operations will be discussed in the next sessions.

Defining "Variables" to be injected

The simplest component that can be injected by the Injector is a variable. To let it know what variables it should inject, their respective types and initial data, you must create a VariableDefinition object for each variable you want to inject and pass it to the Injector via the Injector.SetVariableDefinition() (ramvader.sourceforge.net) method.

The VariableDefinition object's constructor receives a single parameter, which is the initial value of the variable. One important thing to notice is that the type of the variable to be injected is determined by its initial value: if the initial value is an int value, then the injection variable's type will be int; if the initial value is a float value, then the injection varible's type will be float, and so on. All basic types supported by the RAMvader library can be initial values for the injection variables.

The following examples illustrate how to define the variables to be injected by the Injector:

// Consider "EMyVars" an enumeration whose enumerators are used to identify
// each variable to be injected by the Injector.
enum EMyVars
{
    evMyFirstVar,
    evMySecondVar,
    evMyThirdVar,
    evMyFourthVar,
}



// Consider the "injector" variable is a reference for the Injector class...

// Define the "EMyVars.evMyFirstVar" variable as
// an int (Int32) variable with initial value of 123
VariableDefinition myIntVarDefinition = new VariableDefinition( (Int32) 123 );
injector.SetVariableDefinition( EVar.evMyFirstVar, myIntVarDefinition );

// Define the "EMyVars.evMySecondVar" variable as
// a float (Single) variable with initial value of 111.222
VariableDefinition myFloatVarDefinition = new VariableDefinition( (Single) 111.222f );
injector.SetVariableDefinition( EVar.evMySecondVar, myFloatVarDefinition );

// Define the "EMyVars.evMyThirdVar" variable as
// a double (Double) variable with initial value of 123.456
VariableDefinition myDoubleVarDefinition = new VariableDefinition( (Double) 123.456 );
injector.SetVariableDefinition( EVar.evMyThirdVar, myDoubleVarDefinition );

// Define the "EMyVars.evMyFourthVar" variable as
// an unsigned long (UInt64) variable with initial value of 123456
VariableDefinition myUnsignedLongVarDefinition = new VariableDefinition( (UInt64) 123456 );
injector.SetVariableDefinition( EVar.evMyFourthVar, myUnsignedLongVarDefinition );

After learning how to define the injection variables and code caves, you will learn how to inject them in the target process' memory space by using the Injector class.

Defining the "Code Caves" to be injected

Similarly to the way you define injection variables through the use of VariableDefinition objects and calls to the Injector.SetVariableDefinition() (ramvader.sourceforge.net) method, you can define the code caves to be injected by using CodeCaveDefinition objects and making calls to the Injector.SetCodeCaveDefinition() (ramvader.sourceforge.net) method. But that is where the similarity between the definition of injection variables and code cave ends, as you will see.

Code caves are much more complex objects than injection variables, because they are a piece of binary code that can be executed, and that might reference the addresses of injection variables, the addresses of other code caves, and the addresses of other elements in the target process' memory space. Thus, the code cave definitions are actually composed of a list of all small pieces that are glued together to build the final code cave.

Each "small piece" that is used to build a code cave's definition is called a "code cave artifact", which is represented by the CodeCaveArtifact class. This is an abstract class whose specializations know how to generate the actual bytes that will be injected in the target process' memory space (which are the effective "code cave" that goes into the target process' memory space). When the Injector class is performing the injection in the target process' memory space, it goes through the list of code cave artifacts that compose a code cave, and asks each artifact to generate the bytes that it represents. These bytes are added to a buffer in the order they are generated, and after all this processes ends, this buffer is what gets injected into the target process' memory space.

In theory, you can build a list of CodeCaveArtifact objects yourself, and then create a CodeCaveDefinition with that list, but that approach is not recommended. The RAMvader library provides an easier way to craft code cave definitions: by using the CodeCaveBuilder class. This class provides convenient methods that follow a fluid pattern, allowing you to create a code cave definition by chaining special methods that internally generate a list of artifacts. In the end of the process, you can call the CodeCaveBuilder.Build() (ramvader.sourceforge.net) method, which will generate a CodeCaveDefinition for you by using the internally generated list of artifacts. Simple as that.

It is recommended that you check the API Reference for the CodeCaveBuilder class to know about the methods it offers to build the code cave definitions, and what type of artifacts these methods generate (if you want to learn more about the proccess of creating code cave definitions manually). The following list briefly resumes these methods:

  • Bytes(): allows you to insert a sequence of bytes in the code cave. This is currently the most used method to create code caves, as you can copy the bytes that compose a code cave by using it. The recommended approach is to use third-party programs such as OllyDbg or CheatEngine to assemble code caves and copy the bytes corresponding to these assembled instructions, and then pass these bytes to the Bytes() method to compose your code cave.
  • SBytes(): does the same as the Bytes() method, but allows adding signed bytes instead of unsigned ones.
  • VarAddr(): allows you to add to the code cave a sequence of bytes corresponding to an injected variable's address in the target process' memory space. This method automatically deals with endianness and pointer size when adding these bytes.
  • CaveAddr(): allows you to add to the code cave a sequence of bytes corresponding to an injected code cave's address in the target process' memory space. This method automatically deals with endianness and pointer size when adding these bytes.
  • X86Call(): adds a x86 CALL instruction to the code cave, which calls the specified MemoryAddress.
  • X86NearJump(): adds a x86 (near) JUMP instruction to the code cave, which jumps to the specified MemoryAddress.
  • X86FarJump(): adds a x86 (far) JUMP instruction to the code cave, which jumps to the specified MemoryAddress.

Now that you know the theory, let's go to an example of how to build two code cave definitions to be injected in a target process' memory space, assuming the process' program is written for an x86 processor:

/* The following code cave is a simple code cave, which

 * references no injected variables. Its bytes represent the following
 * x86 assembly code:
 *
 * OPCODES | ASSEMBLY INSTRUCTIONS
 * -------------------------------
 * 31 C0   | xor eax,eax
 * B0 05   | mov al,05
 * C3      | ret
 * */
var simpleCodeCaveDefinition = injector.NewCodeCave()
    .Bytes( 0x31, 0xC0, 0xB0, 0x05, 0xC3 )
    .Build();
injector.SetCodeCaveDefinition( ECodeCave.evSimpleCodeCave, simpleCodeCaveDefinition );




/* The following code cave references two variables, identified by

 * the enumerators "EMyVariables.evVar1" and "EMyVariables.evVar2".
 * Its bytes represent the following x86 assembly code (addresses
 * of variables have been replaced by "????????", because there's
 * no way to tell the addresses until the Injector effectivelly
 * injects the code into the target process' memory space):
 *
 * OPCODES | ASSEMBLY INSTRUCTIONS
 * -------------------------------
 * 51                    - push ecx
 * 8B 0D ????????        - mov ecx,[ptrVar1]
 * B4 07                 - mov ah,07
 * 01 C8                 - add eax,ecx
 * 59                    - pop ecx
 * 89 05 ????????        - mov [ptrVar2],eax
 * C3                    - ret 
 * */
var complexCodeCaveDefinition = injector.NewCodeCave()
    .Bytes(0x51, 0x8B, 0x0D)
    .VarAddr( EVar.evMyIntVar )
    .Bytes( 0xB4, 0x07, 0x01, 0xC8, 0x59, 0x89, 0x05 )
    .VarAddr( EVar.evMySecondIntVar )
    .Bytes( 0xC3 )
    .Build();
injector.SetCodeCaveDefinition( ECodeCave.evComplexCodeCave, complexCodeCaveDefinition );

Details about definitions of Code Caves and Injection Variables...

Undefined Code Caves and Variables

Starting from RAMvader 1.3, the Injector class now allows you to define code caves and injection variables during runtime. These entities are identified by enumerators for the Injector, and the enumerations they belong to are in fact defined in the Injector's class generic type parameters. In previous versions, once you had an enumeration of code caves or injection variables, all the enumerators in this enumeration were required to have a definition, or the library would throw an exception while trying to perform an injection procedure.

In RAMvader 1.3 this doesn't happen anymore: you can safely leave one or more code caves and/or injection variables undefined, if you want. The effect on this is that the Injector will treat these undefined code caves/injection variables as if they were non-existant: they will not be injected, and they will not be accounted for when, say, the Injector is counting how many bytes are required for the injection process.

During runtime, if you want, you can undefine a code cave or injection variable that you have already defined. You can do this by calling Injector.ClearCodeCaveDefinition() (ramvader.sourceforge.net) and Injector.ClearVariableDefinition(), respectivelly. You also have the option to clear all code caves at once by calling Injector.ClearAllCodeCaveDefinitions() (ramvader.sourceforge.net), and/or clear all injection variables at once by calling Injector.ClearAllVariableDefinitions() (ramvader.sourceforge.net). The entities that have their definitions "cleared" are considered "undefined" by the Injector, which means that they won't get injected in the target process' memory space.

Updating injected code caves and injected variables

You cannot perform updates on the structure of already-injected Code Caves and on the types of already-Injected Variables. Any attempt to do so while the Injector is injected will fire an InstanceAlreadyInjectedException.

You can, however, undo the injection (by first clearing-up any code on the target process that depends on injected resources that can be extinguished, and then calling Injector.ResetAllocatedMemoryData() (ramvader.sourceforge.net)) and after that you can re-configure the injector the way you want. You can change code caves' and injection variables' definitions at that time.

Injecting into the target process' memory space!

It's time to learn how to effectivelly command the Injector to inject the code caves and variables you have defined.

When everything (code caves, injection vriables and Memory Alteration Sets) is set-up on the Injector and ready, you can perform the injection by calling the Inject() method. This method has two overloads. If you call this method without any parameters, the Injector will automatically try to allocate extra memory on the target process' memory space, and will use that allocated memory to inject the code and variables. The second version of the method receives an address (MemoryAddress value) indicating where the Injector should inject its code caves and variables. This second version can be used if you want to deal with memory allocation yourself, or if for any reason you cannot allocate extra memory in the target process' memory space (e.g., the target process' has some sort of protection which prevents other processes from allocating memory on it).

// INJECTION METHOD 1:
// Automatically takes care of extra memory's allocation, and injects
// the code caves and variables in the allocated space.
injectorObj.Inject();


// INJECTION METHOD 2:
// Lets you take care of WHERE the injector will place the code caves
// and variables it needs to inject.
// In the following example, the Injector places them at address 0x1122AABB.
injectorObj.Inject( new AbsoluteMemoryAddress( 0x1122AABB ) );

This is all that is required for you to realize the injection. The Injector will read all the definitions of code caves and injection variables, generate the bytes that need to be injected in the target process' memory space, and write these bytes to the injection point.

Finally, once you need to clean the memory that has been allocated by the Injector, you can call the ResetAllocatedMemoryData() method. Notice that this method will both clean the Injector methods internal data, preparing it to be used in another injection (if necessary), and it will also release the memory it has allocated on the target process' memory space, so be cautious when trying to perform I/O operations in that memory space (e.g., reading the value of an injected variable after releasing the memory). Also, be very cautious to remove any detours you have injected into the target process' memory space to execute the code of injected code caves: as the memory has been released, the target process' might crash if it tries to execute the code inside the code caves (as the code will cease to exist after the memory has been released). Clearing the target process' memory space of any alterations the host process made on it and restoring the target process' memory space to its original shape is usually done before the ResetAllocatedMemoryData() method is called to prevent the target process from crashing.

It is worth making it clear that if you have used the Inject( MemoryAddress ) method to perform the injection of code caves and variables in an address that you have specified, the Injector will NOT release the memory upon calling the ResetAllocatedMemoryData(), as it has not allocated any memory. In that case, it will only clear its internal data, without performing any memory deallocation. This means that the data that has been injected will not be cleared, and will still be at the same place - and you are resposible for clearing it up (e.g., deallocating it), whenever necessary.

// Release the memory allocated by the Injector (if any)
injectorObj.ResetAllocatedMemoryData();

Dealing with injected variables

After you have performed the injection operation on the target process' memory space, you can use the GetInjectedVariableAddress() method to retrieve the address where a specific variable has been injected into the target process' memory space. Retrieved addresses are given as IntPtr objects. This method will throw exceptions if it is used before the Inject() method is called.

The retrieved address can be used for debugging, or you can use a RAMvaderTarget object to read the variable's value or write a new value to it (update its value).

// Assuming "injectorObj" is an instance of an Injector object..
// Assuming "myTargetProc" is an instance of its associated RAMvaderTarget..

// Retrieve the address of a variable and update its value
IntPtr myVarAddress = injectorObj.GetInjectedVariableAddress( EMyVariables.evPlayerHP );

// Update the value of the variable to 321
myTargetProc.WriteToTarget( myVarAddress, (Int32) 321 );

But wait! There's a better and safer way to perform these reading/writing operations in injected variables. The Injector class offers the ReadVariableValue() and WriteVariableValue() methods, both to ease your task when performing I/O operations to injected variables and also to make it safer, as both of these methods check if the variable's type actually matches the type of the passed parameters, throwing exceptions if the given parameters are wrongly typed. This allows you to easilly detect logic errors when writing RAMvader-based programs that use variable injection.

// Read the value of an injected variable, double it and
// write it back to the target process' memory space
Int32 playersHP;
injectorObj.ReadVariableValue( EMyVariables.evPlayerHP, ref playersHP );

Int32 newHP = playersHP * 2;
injectorObj.WriteVariableValue( EMyVariables.evPlayerHP, newHP );

It is worth noticing that both the ReadVariableValue() and WriteVariableValue() methods return a boolean value indicating if the operation was a success. These methods use the RAMvaderTarget's class ReadFromTarget() and WriteToTarget() methods under the hood.

Executing injected code caves

When you inject code caves into the target process' memory space, you need to alter the flow of execution of the process' program so that it executes your code, somehow. When injecting code caves, this is usually done by replacing one or more of the target process' program's instructions by an Assembly's JMP (jump) or a CALL instruction, which diverts the flow of execution to the address of a specific code cave.

To help you with that task, the Injector offers some methods for writing CALL and JMP instructions. Currently, the RAMvader library only supports the generation of x86 instructions. You should check the API reference for each of the methods that write the detour instructions (CALL or JMP) to see how each method works. The available methods for writing such instructions are the following:

There is a new and better way of writing these detours to code caves, and it is recommended that you use that new way. This new way is by using the Memory Alteration Sets to delegate the task of creating the detours to the Injector class. By doing this, you also gain the option to enable/disable the code cave detours whenever you want, and without having to keep track of the target process' original bytes (as the Injector already does this for you).

For both methods (by manually writing CALL/JUMP instructions or by using the new Memory Alteration Sets method), there are some caveats we need to be aware when using CALL instructions.

First, if the code cave is called through a CALL instruction from Assembly, you must write the code cave like you would write a function in Assembly. This effectivelly means that you must remember that at the end of the code cave you must use a RET instruction to return from the function to the point where the call to it was made.

Secondly, there is a parameter for the methods that write CALL instructions that specify the size of the instruction that is being replaced by the CALL instruction. The Injector knows that a (x86) CALL instruction takes 5 bytes in memory, and uses the size that you give to fill any extra bytes after these 5 bytes with NOP instructions, so that the program won't crash when the call to the code cave returns. If the instruction you are trying to replace is less that 5 bytes, the method will fail. In that case, you might need to replace more than one instruction until the size of this set of replaced instruction reaches 5 bytes or more.

Also, when writing a CALL/JUMP detour, always remember to keep the program's stacks balanced and ensure that the instructions surounding the CALL/JUMP instruction will be executed correctly when the code cave returns from its execution. Keep the instructions surounding the CALL/JUMP instruction consistent! Remember that the CALL instruction pushes the return address of the procedure to the stack, which must be manually popped out of the stack or should be popped by using the RET instruction (which is the recommended way).

The following example places a CALL instruction at the point 0xAABBCCDD of the target process' program to call the code cave identified by the "ECodeCaves.evMyCodeCave" enumerator, and tells the Injector that this CALL instruction will be replacing an instruction that is 6 bytes long (e.g., the instruction "mov ecx, dword ptr [1122AABB]" would be written in an x86-based system as the following 6-bytes sequence: 8B 0D BB AA 22 11). The Injector will then know that it needs to write the 5-bytes CALL instructions followed by 1 NOP instruction (each NOP instruction occupies 1 byte only).

MemoryAddress writePoint = new AbsoluteMemoryAddress(0xAABBCCDD);
MemoryAddress callTarget = injector.GetInjectedCodeCaveAddress( ECodeCaves.evMyCodeCave );
injector.WriteX86CallInstruction( writePoint, callTarget, 6 );

It is worth noticing that these methods call the WriteToTarget() method under the hood, and they return a boolean value to indicate its success.

Using Memory Alteration Sets

The Memory Alteration Sets feature has been implemented on RAMvader version 1.2 and offers several benefits.

First benefit is: you can group many writing operations, allowing you to enable and disable them at any time on the target process. "Enabling a set" will perform all the write operations registered for a set. "Disabling" a set will revert all the alterations that the set might have inflicted to the target process' memory space.

Notice: currently, the execution of these operations are non-atomic, so use this with caution.

When you instantiate a "Memory Alteration" object (which will be explained bellow), the target process' memory space will be read by the instantiated object, and it will keep a snapshot of the target process' memory space that might be altered by it. So, when you ask the Injector to disable a Memory Alteration Set, it will verify which points of the target process' memory space this set affects, and will return all of these "affected" points to their original values (retrieved from the target process' memory space at the moment the "memory alteration objects" have been instantiated). So there's another benefit: you do not have to keep track of the target process' original bytes when you want to disable an alteration you have made to the target process' memory, as the Injector will automatically do it for you. An example of use: you can create a game trainer which allows the player to enable and disable cheats (every cheat can be created as a memory alteration set, and then be freely enabled or disabled by the player).

The RAMvader library comes with a bunch of classes that represent memory alterations that the Injector can handle. These classes are:

  • MemoryAlterationNOP: used to replace instructions on the target process' memory space by NOP instructions.
  • MemoryAlterationPoke: used to replace instructions or data on the target process' memory space by a predefined array of bytes.
  • MemoryAlterationX86Call: used to replace instructions on the target process' memory space by a x86 CALL instruction.
  • MemoryAlterationX86FarJump: used to replace instructions on the target process' memory space by a x86 FAR JUMP instruction (JMP, JA, JB, JG, JL, JE, JNE).
  • MemoryAlterationX86NearJump: used to replace instructions on the target process' memory space by a x86 NEAR JUMP instruction (JMP, JA, JB, JG, JL, JE, JNE). Note: Unless you know exactly what you are doing, it is recommended to use FAR JUMPS instead of NEAR JUMPS, as the NEAR JUMPS are very limited and might easily break when using Injector's automatic memory allocation.

Now that you know how the Memory Alteration Sets might help you, let's teach you how to use them. First thing to know: you can use the AddMemoryAlteration() and RemoveMemoryAlteration() methods to register and unregister memory alterations for a specific set, respectively. Then, after the Injector's Inject() method has been called, you can use the SetMemoryAlterationsActive() method to enable or disable a specific Memory Alteration Set.

Let's go for an example. Suppose you have a game where you need to do three things to make the player have infinite money: NOP the instruction that decreases the player's money when he/she buys something on a store, call one of you code caves that keeps updating the player's money to 999, and NOP another instruction that decreases the player's money when he needs to pay his rent in the game. Here's an illustration of how to define that cheat - which needs three specific alterations to the game's memory - as a memory alteration set:

// Before injecting the code into the game's memory space, we should register all
// the memory alterations the trainer could make to the game's memory space.
// Here we're showing only the alterations that will be registered for a
// memory alteration set identified by some
// invented identifier called "EAlterationSets.evAlterationMoneyHack"

// First, let's create the objects representing the memory alterations
MemoryAlterationNOP firstAlteration = new MemoryAlterationNOP( myRAMvaderTarget, new AbsoluteMemoryAddress( 0x11223344 ), 3 );
MemoryAlterationPoke secondAlteration = new MemoryAlterationPoke( myRAMvaderTarget, new AbsoluteMemoryAddress( 0xAABBCCDD ), new byte [] { 0xE7, 0x03, 0x00, 0x00 } );
MemoryAlterationNOP thirdAlteration = new MemoryAlterationNOP( myRAMvaderTarget, new AbsoluteMemoryAddress( 0x0BABACA0 ), 6 );

// Now register all of the created alterations to the set of memory alterations
// that represent the trainer's "money hack" 
myInjectorObj.AddMemoryAlteration( EAlterationSets.evAlterationMoneyHack, firstAlteration );
myInjectorObj.AddMemoryAlteration( EAlterationSets.evAlterationMoneyHack, secondAlteration );
myInjectorObj.AddMemoryAlteration( EAlterationSets.evAlterationMoneyHack, thirdAlteration );

...

// When the player clicks the "Enable MONEY HACK" button, you should ACTIVATE the
// memory alteration set as follows. This will perform all the alterations registered
// for the "EAlterationSets.evAlterationMoneyHack" memory alteration set.
myInjector.SetMemoryAlterationsActive( EAlterationSets.evAlterationMoneyHack, true );

...

// When the player clicks the "Disable MONEY HACK" button, you should DEACTIVATE the
// memory alteration set as follows. This will undo all the alterations registered
// for the "EAlterationSets.evAlterationMoneyHack" memory alteration set.
// The "undo" process returns all of the bytes affected by all the alterations
// registered for the "EAlterationSets.evAlterationMoneyHack" memory alteration set
// to their original values (obtained when the Injector.Inject() method was called)
myInjector.SetMemoryAlterationsActive( EAlterationSets.evAlterationMoneyHack, false );

Memory Alteration Sets are really just a small "engine" that avoids that game trainer coders recreate all of the repetitive code that needs to be written in all game trainers in order to enable/disable cheats on the target game's process.

Customizing injected bytes for debugging

The Injector class' default behaviour when injecting its data is to place the code caves and injection variables obbeying the following rules:

  • At the injection point, the bytes of all the code caves are injected sequentially, in the order the identifiers for these code caves are declared. Each code cave is separated by a sequence of bytes, which can be customized by calling the SetCodeCavesSeparationBytes() method.
  • After all the code caves have been written, the Injector writes a sequence of bytes that will separate these code caves from the variables. This sequence of bytes can be defined by calling the SetVariablesSectionSeparationBytes().
  • After these bytes that separate the code caves from the injected variables, the Injector places all variables sequentially, in the order the identifiers for these variables were declared, without any separation between them.

The SetCodeCavesSeparationBytes() and SetVariablesSectionSeparationBytes() can be used to customize the bytes output by the Injector into the target process' memory space, when injecting the necessary data. The separators placed by the Injector are used to organize the injected data in a way that is easy to look at with debugging tools and differentiate between each one of the code caves and the variables section that have been injected. You can call these methods to set the separation bytes as you want. You might use this, for example, to increase the space between code caves so that you can write more code between them using a debugging tool, or maybe you can just set these separation bytes to zero-length arrays, so that the Injector makes the injected data as packed as possible - it just gets a little bit harder to see where each code cave starts and ends, and where the variables section starts. Use it as you find better.


Related

Wiki: Home