Modding[C#][CheatEngine] DIY Simple Game Trainer Tutorial
Posted:

Modding[C#][CheatEngine] DIY Simple Game Trainer TutorialPosted:

NightFyre
  • Winter 2020
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
INDEX

    Overview
  • Prerequisites
  • Software Required

    CheatEngine
  • Finding addresses
  • Finding Pointers

    Visual Studio
  • Form Configuration
  • Labels , Textbox's and Buttons
  • Checkbox's
  • Creating events
  • Read / Write Memory (memory.dll)





GOAL
Create an AssaultCube Trainer that will allow users to edit Health, Armor and Ammo values in real time. Both by interacting with a Form and using Hotkeys

Addresses Required:
- Player Health
- Player Armor
- Player Ammo

Pointers Required:
- PlayerPointer
This will give us offsets for everything else we need. (This will also give us information for much more than we need ... Like player coordinates for instance)



OVERVIEW

In this tutorial we will be using a source code I have uploaded on GITHUB as reference material.
We will be modifying the game AssaultCube which is an OpenSource game , free to download.

Prerequisites:
- PC
- CheatEngine Knowledge
- Creative Mind (not required , but will be your biggest limitation)


Software Required:
- Microsoft Visual Studio
- Cheat Engine
- AssaultCube | [source]
- Github Repo | [link] *not required as I'll be sure to include code snippets ... but having a full source to read from is always helpful when learning.



CHEAT ENGINE

Finding Addresses:
We will start this tutorial out by finding addresses for our players Health, Armor and Ammo.
Start out by loading AssaultCube > Single Player > Empty Map > Douze


Load CheatEngine and hook to AssaultCube


Lets start scanning values we see in game on screen , Ammo = 20


Notice how we have almost 3k results? We can narrow down our results by doing another scan. Lets do this now.
The results narrowed down to 2500 possible addresses ... lets shoot our gun 1 time and then change our scan value to 19


If you followed the steps correctly , you should have less than 10 results. Add them to our address list now.
Change the value of the first result to 20 and see what happens in game.


Ok lets freeze this address now and shoot our gun a few times , see what happens in game and with our other addresses in the address list.
*to freeze a value in CheatEngine just press the left-hand checkbox on the address line.

Notice how our other address is still counting down? You can continue shooting till this value hits 0 but you will still have unlimited Ammo.


Lets put both values to 20 and reverse our freeze function to the address that was still counting down.
It seems our ammo count in game still counts down , for now we will keep these addresses locked together when freezing our ammo value.


Follow this same procedure to find values for Health and Armor (I have faith in you , remember how I said this requires creativity?)

Ok .. Save your current data in CheatEngine and set that file aside for later.. Lets restart AssaultCube and move onto the next hurdle in creating a trainer



Finding Pointers
Ok so some of you might be thinking at this point "Ok ... this is entirely too easy .. Is this really all there is to it???"
and you would be half correct in that ... as this can sometimes be all you need and will only have to find values again when the game is updated.

However ... most games nowadays use pointers and offsets ... a way of obfuscating memory to prevent cheating.
This makes the addresses of game information change upon game launch , restart , death , mission restart ... etc.
"Now you might be thinking ... welp ... this gonna get tedious"
and again you would be half correct in that thought as well ... lol

Because of this change in address location ... the game still needs a way to correctly handle the structure of the memory ... therefore pointers will lead to addresses containing a lot of information in 1 place.
Now that address might change ... but the pointer address wont... Same can be said for the information within the pointer (Offsets)
This can be very confusing ... as you can have layers of offsets until you finally get to the address you want to alter a value.
Thankfully cheat engine makes this a bit easier. Although the process involved can be pretty tricky and time consuming ... (including space required for the data depending on game size)
Ok... sorry for the lengthy explanation but this information can be very useful ... we are trying to learn aren't we?

Lets check out this screenshot


Right ... we got a problem don't we? Lets get our addresses for health, armor and ammo back.
Now that we have our addresses back ... we can start the lengthy process of finding pointers and offsets.
Now we could go about this 2 ways ... jumping into a pointer scan OR generating pointermaps and getting a FOR SURE POINTER . . .
idk about you .. but I'd rather do it the right way and have something I can rely on for a while.


LVL 4 Pointer Map Method
- Find Address > Save Address and # it
- Pointer Scan > Generate Pointer Map > Save "AddressMap#"
- Restart Game and repeat this process 3 times
- Find Address > Pointer Scan > Tick "compare results with other saved pointermap(s)"
- Select saved pointer maps and addresses (see why we numbered everything?)
- Now do a pointer scan



Ok so we got some results , lets add some to our address list by double clicking on them.
*Make sure to only grab addresses that start with (GameName.Exe) Never use threadstacks ....
Restart AssaultCube and see if they contain the Values


Alright so if you've followed all the steps you should have some pointers that will always load the correct address.
At this point we can go ahead an start making a trainer for our game. Note all of these pointers down.
"ac_client.exe"+00109B74,0x150 = PrimaryMagAmmo



Congrats! You made your first cheat code

Stay Tuned For Part 2 Where we will create a Trainer using Windows Visual Studio 2019
(I need to work on formatting , picture organization and the github repository)

In the meantime ... Check out this video I made on CodeInjection with CheatEngine


Last edited by NightFyre ; edited 1 time in total

The following 4 users thanked NightFyre for this useful post:

Sean (06-13-2021), Wraith (06-13-2021), Johnny (06-13-2021), Reidso (06-13-2021)
#2. Posted:
NightFyre
  • Christmas!
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Visual Studio Part 1


PART 1 SOURCE: GITHUB REPOSITORY

Form Configuration
Open Visual Studio > New Project > Windows Forms App C# (name this whatever you would like , but for the sake of this tutorial we will name the project "AssaultCubeTrainer-TTG"
Save this wherever you would like.

    Project > Manage NuGet Packages...
    Browse > Memory.Dll
    Install => Memory.dll.x64 by NeWaGe


Memory.dll Inititialization

1. First add an application manifest file.
Project > Add New Item... > Application Manifest File (Windows Only)

2. Open the app.manifest file from your Solution Explorer window.

3. Edit line 19 where it says "level=" change "asInvoker" to "requireAdministrator". Save and close.

4. Open NuGet manager and Browse for "System.Security.Principal.Windows", install this.

5. Your trainer should match your game's platform. If compiling with Any CPU, uncheck the prefer 32bit checkbox in Build Project Properties if you need x64.
Ex: If game is x86, trainer should be x86. If game is x64, trainer should be x64.




Labels , Textbox's , Buttons & Checkbox's
Ok ... now that we got some of the basic stuff imported .. lets go ahead and design our form.
In our toolbox on the left-hand side type in "Label"


Add this to our form anywhere. Click on the label and navigate to the properties toolbar
Change the text to "AMMO:"


Grab a textbox from the toolbox and drag it into our form next to the label
Do this again with a Button, another label and a checkbox.


Change the text for our button and textbox using the properties tab like we did with our first label.
Button => SEND
Checkbox => Unlimited Ammo



Creating Events
Double Click our Button this should bring us into CodeView for our MainForm


at the top of our code window type
using memory;


directly under public partial class "Form1" we can start initializing some components

public Mem m = new Mem();
bool ProcOpen = false;
private const string AssaultCubeProcess = "ac_client.exe";


We need to add some things when we load our form as well

int PID = m.GetProcIdFromName(AssaultCubeProcess);
if (PID > 0)
{
   m.OpenProcess(PID);
   MessageBox.Show("Connected to Game");
}



Ok So basically what we are doing here is importing our memory.dll package and creating a nickname for our process. When we load up our form we are gonna determine whether or not the game is process is running and displaye a message box window with a message to let us know whether we are connected to the process or not.

At this point you should head up to configuration manager and set the platform of our application to x64
*If its not listed you should be able to add one


Go ahead and start the application , if you have Assault Cube running you should get a sweet message that says "Connected to Game". Close the application



At this point it would be a good idea to start adding other UI elements that you would like included.
For the sake of simplicity .... for this tutorial .. I will only be covering the basic usage of Buttons , Labels , Textboxes and Checkboxes.


Overview of what our code should look like

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Memory;

namespace AssaultCubeTrainer_TTG
{
    public partial class Form1 : Form
    {
        public Mem m = new Mem();
        bool ProcOpen = false;
        private const string AssaultCubeProcess = "ac_client.exe";

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            int PID = m.GetProcIdFromName(AssaultCubeProcess);
            if (PID > 0)
            {
                m.OpenProcess(PID);
                MessageBox.Show("Connected to Game");
            }
            else
            {
                MessageBox.Show("Game Not Found");
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {

        }

        private void button2_Click(object sender, EventArgs e)
        {

        }

        private void button3_Click(object sender, EventArgs e)
        {

        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void checkBox2_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void checkBox3_CheckedChanged(object sender, EventArgs e)
        {

        }
    }
}





Visual Studio Part 2


PART 2 SOURCE: [url=https://github.com/xCENTx/AssaultCubeTrainer-TTG/tree/main/PART%202%20-%20Read%20%20%26%20Write%20Memory[/url]

Read & Write Proc Memory


OK ... so believe it or not. Most of the hard stuff is done at this point. Now we just need to tie everything together. Which sometimes can be a chore in and of itself. Thankfully , for this instance It will be relatively easy.

Lets start with buttons , we have 3 of them. Ammo , Health and Armor.
For the sake of this tutorial ... We want to input a number in one of our text boxes , press the send button and have our ammo , health or armor adjusted by the input value.

Write Memory to Ammo Address with a Value of TextBox Input
Heres the code

m.WriteMemory("base+00109B74,0x150", "int", textBox1.Text);

"base+00109B74,0x150" = Ammo Address
"int" = Indicates that we want to send the value in Integer Format
textBox1.Text = This indicates that the value sent will be whatever value is in our Textbox1 Field.

We can follow this same process for any other buttons that have been added. In our case the Health and Armor. Don't forget to change input based on the textbox and offset

For Checkboxes we will add a little spin , so you can learn more about how you can write to memory in different ways.
Our checkboxes have an "Unlimited ***" Labeled next to them ... so we want to Freeze the value this time around. To do this we will have to define a variable that is set to read what our current value is , so that we can freeze this exact same value. That way when we unfreeze ... everything returns to normal.
At the same time , we need to make sure that when we Uncheck the checkbox , that our freeze event ends.

If Checkbox is Checked => Freeze Current Value at Ammo Address .... Otherwise we want this value unfrozen

var CurrentValue = m.ReadInt("base+00109B74,0xFC");
if (checkBox3.Checked)
{
   m.FreezeValue("base+00109B74,0xFC", "int", CurrentValue.ToString());
}
else
{
   m.UnfreezeValue("base+00109B74,0xFC");
}


See how this all works? It can be a bit complicated at first. But over time it just starts to make sense more and more. especially if you are interested enough to test your knowledge each and every day by trying new things

For the last piece of this tutorial ... I wanted to implement a Timer which will read memory and display the current value in our application. Much like what is already done in game Via the HUD.

In the toolbox search for "timer" and add this to the form.
Go to properties , make sure it is enabled and name it "ProcessTimer".
Once this is done , double click the timer on the form window to generate our Timer_Tick Event.

Once in code view all we have to do is exactly the same thing we did in our form load. We want to grab the process id and if the process is running .. we will start a open the process to be read.

int PID = m.GetProcIdFromName(AssaultCubeProcess);
if (PID > 0)


Now all we have to do tell our labels what they should be doing. This is as simple as writing the label name = "readmemory"

m.OpenProcess(PID);
label2.Text = m.ReadInt("base+00109B74,0x150").ToString();
label3.Text = m.ReadInt("base+00109B74,0xF8").ToString();
label5.Text = m.ReadInt("base+00109B74,0xFC").ToString();


I will update this to include more information as I have brushed over a lot. and decided to write this around midnight. At the same time ... I don't want to update this too much as once i start updating things i start simplifying stuff i've done which makes it harder to understand whats really going on. I want this to stay as barebones as possible.

If anything ... I will most certainly be commenting throughout the source code on the Repository

FULL SOURCE CODE
this tutorial is longer than this code .... js

using System;
using System.Windows.Forms;
using Memory;

namespace AssaultCubeTrainer_TTG
{
    public partial class Form1 : Form
    {
        ///Imports and Config
        #region

        public Mem m = new Mem();
        private const string AssaultCubeProcess = "ac_client.exe";

        #endregion
       
        ///Form Load
        #region

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            int PID = m.GetProcIdFromName(AssaultCubeProcess);
            if (PID > 0)
            {
                m.OpenProcess(PID);
                MessageBox.Show("Connected to Game");
            }
            else
            {
                MessageBox.Show("Game Not Found");
            }
        }

        #endregion

        ///Memory Reader
        #region

        private void ProcessTimer_Tick(object sender, EventArgs e)
        {
            int PID = m.GetProcIdFromName(AssaultCubeProcess);
            if (PID > 0)
            {
                m.OpenProcess(PID);
                label2.Text = m.ReadInt("base+00109B74,0x150").ToString();
                label3.Text = m.ReadInt("base+00109B74,0xF8").ToString();
                label5.Text = m.ReadInt("base+00109B74,0xFC").ToString();
            }
        }

        #endregion

        ///Buttons
        #region

        //Ammo Button
        private void button1_Click(object sender, EventArgs e)
        {
            m.WriteMemory("base+00109B74,0x150", "int", textBox1.Text);
        }

        //Health Button
        private void button2_Click(object sender, EventArgs e)
        {
            m.WriteMemory("base+00109B7,0xF8", "int", textBox2.Text);
        }

        //Armor Button
        private void button3_Click(object sender, EventArgs e)
        {
            m.WriteMemory("base+00109B74,0xFC", "int", textBox3.Text);
        }

        #endregion

        ///Checkboxes
        #region
       
        //Unlimited Ammo
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            var CurrentValue = m.ReadInt("base+00109B74,0x150");
            if (checkBox1.Checked)
            {
                m.FreezeValue("base+00109B74,0x150", "int", CurrentValue.ToString());
            }
            else
            {
                m.UnfreezeValue("base+00109B74,0x150");
            }
        }

        //Unlimited Health
        private void checkBox2_CheckedChanged(object sender, EventArgs e)
        {
            var CurrentValue = m.ReadInt("base+00109B74,0xF8");
            if (checkBox2.Checked)
            {
                m.FreezeValue("base+00109B74,0xF8", "int", CurrentValue.ToString());
            }
            else
            {
                m.UnfreezeValue("base+00109B74,0xF8");
            }
        }

        //Unlimited Armor
        private void checkBox3_CheckedChanged(object sender, EventArgs e)
        {
            var CurrentValue = m.ReadInt("base+00109B74,0xFC");
            if (checkBox3.Checked)
            {
                m.FreezeValue("base+00109B74,0xFC", "int", CurrentValue.ToString());
            }
            else
            {
                m.UnfreezeValue("base+00109B74,0xFC");
            }
        }

        #endregion
    }
}
#3. Posted:
Wraith
  • Trusted Seller
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
Motto: https://wraith.to
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
Ok so following your tutorial I was able to successfully find the offsets for money in Borderlands 2 although I am having trouble implementing it in VS.

Here is the screenshot of the offset info along with it working in-game (I have restarted 3-4 times to be sure it works).


Here is the error I am getting


I am not sure if I have just misunderstood how the offsets work but I put "6x2A0" as "2A0" is the 6th offset in the pointer map so I assumed that's how it worked as you only have 1 offset in yours.

If you have some time free and are willing to help me out, that would be great.
#4. Posted:
NightFyre
  • Challenger
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Wraith wrote Ok so following your tutorial I was able to successfully find the offsets for money in Borderlands 2 although I am having trouble implementing it in VS.....

I am not sure if I have just misunderstood how the offsets work but I put "6x2A0" as "2A0" is the 6th offset in the pointer map so I assumed that's how it worked as you only have 1 offset in yours...


Thats a great question buddy. So each offset would prefix with "0x" . That's my bad for not bringing that up. So you are going to want to add each and every offset in your pointer in order from bottom to top.

Try this out and let me know how it works out for ya

m.WriteMemory("base+015AB02C,0x0,0xC0,0xE0,0x36C,0x1C4,0x4,0x2A0", "int", textBox1.Text);
#5. Posted:
Wraith
  • Graphics King
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
Motto: https://wraith.to
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
I am still getting the same error.


I think it is definitely something I am doing wrong in VS. I am using a C# Windows form app (.NET framework) with the x64 memeory.dll which may be the problem due to the game being 32bit although I was getting errors when using the 32bit memory.dll.
#6. Posted:
NightFyre
  • Challenger
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Wraith wrote I am still getting the same error....

I think it is definitely something I am doing wrong in VS. I am using a C# Windows form app (.NET framework) with the x64 memeory.dll which may be the problem due to the game being 32bit although I was getting errors when using the 32bit memory.dll.


So i just made up a quick trainer using memory.dll x64. I was able to get it to work using this code.

if (MoneyTextBox.Text != "")
   {
      m.WriteMemory("base+0x015AB02C,0x0,0xC0,0xE0,0x36C,0x1C4,0x4,0x2A0", "int", MoneyTextBox.Text);
   }


Here's an image



Although ... with a game like borderlands. I highly recommend taking things a step further and organizing your offsets in a class. You will have many. Health , max health , ammo for each gun type , money , skill points ... the list goes on.

Heres a picture showing what I mean


Here is the full source

Mem m = new Mem();
private const string BORDERLANDS2PROCESS = "Borderlands2.exe";

public static class Offsets
{
   //Player Info
   public const string Money = "0x015AB02C,0x0,0xC0,0xE0,0x36C,0x1C4,0x4,0x2A0";
}

public Form1()
{
   InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
   int PID = m.GetProcIdFromName(BORDERLANDS2PROCESS);
   if (PID > 0)
   {
      m.OpenProcess(PID);
      MessageBox.Show("Connected to Game");
   }
}

private void MoneyButton_Click(object sender, EventArgs e)
{
   if (MoneyTextBox.Text != "")
   {
      m.WriteMemory($"Borderlands2.exe+{Offsets.Money}", "int", MoneyTextBox.Text);
   }
}

private void FreezeMoney_checkBox_CheckedChanged(object sender, EventArgs e)
{
   var oldMoneyValue = m.ReadInt($"Borderlands2.exe+{Offsets.Money}");
   var newMoneyValue = oldMoneyValue;
   if (FreezeMoney_checkBox.Checked)
   {
      m.FreezeValue($"Borderlands2.exe+{Offsets.Money}", "int", newMoneyValue.ToString());
   }
   else
   {
      m.UnfreezeValue($"Borderlands2.exe+{Offsets.Money}");
   }
}
#7. Posted:
Wraith
  • Rated Awesome
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
Motto: https://wraith.to
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
Ok gotcha thanks, I will see if I can get mine to work and if not then I am doing something wrong during the setup of actually making the form rather than the actual code itself.
#8. Posted:
Wraith
  • Rated Awesome
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
Motto: https://wraith.to
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
I managed to get it working and I also made it so it reads the value and displays it in the text box when the box isn't selected which is cool. I will be adding the other currencies along with a bunch of other things but really this is just a project which I am hoping will give me a better understanding of offsets and C#.

Here is a screenshot of the trainer so far
#9. Posted:
NightFyre
  • Summer 2020
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Status: Offline
Joined: Aug 29, 201212Year Member
Posts: 193
Reputation Power: 1338
Wraith wrote

I managed to get it working and I also made it so it reads the value and displays it in the text box when the box isn't selected which is cool. I will be adding the other currencies along with a bunch of other things but really this is just a project which I am hoping will give me a better understanding of offsets and C#.

Here is a screenshot of the trainer so far


DUDE! HELL YEA! That looks so dope as well. Very nice design I have to admit. Makes me want to work on my design skills. I can make my code look neater than my UI lol. I'm really happy you got it figured out

Its pretty fun stuff hey?
#10. Posted:
Wraith
  • Rated Awesome
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
Motto: https://wraith.to
Status: Offline
Joined: Sep 29, 20177Year Member
Posts: 5,119
Reputation Power: 66014
Motto: https://wraith.to
NightFyre wrote
Wraith wrote

I managed to get it working and I also made it so it reads the value and displays it in the text box when the box isn't selected which is cool. I will be adding the other currencies along with a bunch of other things but really this is just a project which I am hoping will give me a better understanding of offsets and C#.

Here is a screenshot of the trainer so far


DUDE! HELL YEA! That looks so dope as well. Very nice design I have to admit. Makes me want to work on my design skills. I can make my code look neater than my UI lol. I'm really happy you got it figured out

Its pretty fun stuff hey?


Thank you! yea I wanted something that looked flat and modern although I'm probably going to change a few things to make them look a little better. I probably spent way more time on the design than the code but the code works so that's something hahaha. I appreciate the help and yeh it is pretty damn fun although generating all of those pointermaps can be annoying hahaha.
Users browsing this topic: None
Jump to:


RECENT POSTS

HOT TOPICS