Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Cheat Engine Forum Index
PostGo back to topic
KryziK
Expert Cheater
Reputation: 3
Joined: 16 Aug 2009
Posts: 199

PostPosted: Sun Jan 16, 2011 6:59 pm    Post subject: C# Trainer Tutorial (With Example)

This code DOES work, but some of it is outdated/poorly written. I'm in the process of updating but it may never make it to the forums depending on how busy I stay. I'm quite new to C#, so I apologize for the inconvenience.

Hello!

I've recently started using C# for making trainers, and I thought I'd show others how to do it.

Before we begin, the memory functions that I am using, excluding:
    Memory: ReadPointer(), WritePointer(), PID(), BaseAddressH(), BaseAddressD()
    Addr: ToHex(), ToDec(), Make()

ARE NOT MINE. They were found HERE. In other words, I only wrote the funtions listed in bold, along with the code for the buttons. With that out of the way, let's begin!

Start a new WindowsFormsApplication. Name it whatever you'd like. I'm naming mine CET_Trainer, because we will be making a trainer for Cheat Engine's Tutorial-i386.exe (The new one).



Now, under the Solution Explorer, find Form1.cs. Rename it to 'TrainerForm.cs'.
This step is optional. I only changed it because I think Form1.cs is ugly and not very descriptive.



Next, go to your Form Builder, under properties, and change:
    Name to 'hForm'
    Text to '[Trainer] Cheat Engine Tutorial'

You can pick and Name and Text you want, but I recommend my suggestions so it's easier to follow the tutorial.
Note: You may have to adjust the size of the window a little bit so that the whole title is shown.



Now, create a button on your form, and change:
    Name to 'hButton_Step2'
    Text to 'Complete Step 2'

Again, you can change these to whatever you prefer.



Double-Click on your Button, and an Event will be created.
This event will be triggered when the button is clicked.
A code window should have opened up, and should look like this:



We will come back to this event later. First, we need to add our Memory Functions.
Add these to the top of the file, under the other using statements:
Code:
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Globalization;




These will allow us additional functionality such as getting a ProcessID from it's name.

Now, copy this code, and paste it under the Form Class, as shown:
Code:
class MemoryAPI
{
   [Flags]
   public enum ProcessAccessType
   {
      PROCESS_TERMINATE = (0x0001),
      PROCESS_CREATE_THREAD = (0x0002),
      PROCESS_SET_SESSIONID = (0x0004),
      PROCESS_VM_OPERATION = (0x0008),
      PROCESS_VM_READ = (0x0010),
      PROCESS_VM_WRITE = (0x0020),
      PROCESS_DUP_HANDLE = (0x0040),
      PROCESS_CREATE_PROCESS = (0x0080),
      PROCESS_SET_QUOTA = (0x0100),
      PROCESS_SET_INFORMATION = (0x0200),
      PROCESS_QUERY_INFORMATION = (0x0400)
   }

   [DllImport("kernel32.dll")]
   public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);

   [DllImport("kernel32.dll")]
   public static extern Int32 CloseHandle(IntPtr hObject);

   [DllImport("kernel32.dll")]
   public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);

   [DllImport("kernel32.dll")]
   public static extern Int32 WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesWritten);
}

public class Memory
{
   public Memory()
   {
   }

   public Process ReadProcess
   {
      get
      {
         return m_ReadProcess;
      }
      set
      {
         m_ReadProcess = value;
      }
   }
   private Process m_ReadProcess = null;
   private IntPtr m_hProcess = IntPtr.Zero;

   public void Open()
   {
      MemoryAPI.ProcessAccessType access = MemoryAPI.ProcessAccessType.PROCESS_VM_READ
      | MemoryAPI.ProcessAccessType.PROCESS_VM_WRITE
      | MemoryAPI.ProcessAccessType.PROCESS_VM_OPERATION;
      m_hProcess = MemoryAPI.OpenProcess((uint)access, 1, (uint)m_ReadProcess.Id);
   }

   public void CloseHandle()
   {
      int iRetValue;
      iRetValue = MemoryAPI.CloseHandle(m_hProcess);
      if (iRetValue == 0)
         throw new Exception("CloseHandle Failed");
   }

   public byte[] Read(IntPtr MemoryAddress, uint bytesToRead, out int bytesRead)
   {
      byte[] buffer = new byte[bytesToRead];
      IntPtr ptrBytesRead;
      MemoryAPI.ReadProcessMemory(m_hProcess, MemoryAddress, buffer, bytesToRead, out ptrBytesRead);
      bytesRead = ptrBytesRead.ToInt32();
      return buffer;
   }

   public byte[] PointerRead(IntPtr MemoryAddress, uint bytesToRead, int[] Offset, out int bytesRead)
   {
      int iPointerCount = Offset.Length - 1;
      IntPtr ptrBytesRead;
      bytesRead = 0;
      byte[] buffer = new byte[4]; //DWORD to hold an Address
      int tempAddress = 0;

      if (iPointerCount == 0)
      {
         MemoryAPI.ReadProcessMemory(m_hProcess, MemoryAddress, buffer, 4, out ptrBytesRead);
         tempAddress = Addr.ToDec(Addr.Make(buffer)) + Offset[0]; //Final Address

         buffer = new byte[bytesToRead];
         MemoryAPI.ReadProcessMemory(m_hProcess, (IntPtr)tempAddress, buffer, bytesToRead, out ptrBytesRead);

         bytesRead = ptrBytesRead.ToInt32();
         return buffer;
      }

      for (int i = 0; i <= iPointerCount; i++)
      {
         if (i == iPointerCount)
         {
            MemoryAPI.ReadProcessMemory(m_hProcess, (IntPtr)tempAddress, buffer, 4, out ptrBytesRead);
            tempAddress = Addr.ToDec(Addr.Make(buffer)) + Offset[i]; //Final Address

            buffer = new byte[bytesToRead];
            MemoryAPI.ReadProcessMemory(m_hProcess, (IntPtr)tempAddress, buffer, bytesToRead, out ptrBytesRead);

            bytesRead = ptrBytesRead.ToInt32();
            return buffer;
         }
         else if (i == 0)
         {
            MemoryAPI.ReadProcessMemory(m_hProcess, MemoryAddress, buffer, 4, out ptrBytesRead);
            tempAddress = Addr.ToDec(Addr.Make(buffer)) + Offset[1];
         }
         else
         {
            MemoryAPI.ReadProcessMemory(m_hProcess, (IntPtr)tempAddress, buffer, 4, out ptrBytesRead);
            tempAddress = Addr.ToDec(Addr.Make(buffer)) + Offset[i];
         }
      }

      return buffer;
   }

   public void Write(IntPtr MemoryAddress, byte[] bytesToWrite, out int bytesWritten)
   {
      IntPtr ptrBytesWritten;
      MemoryAPI.WriteProcessMemory(m_hProcess, MemoryAddress, bytesToWrite, (uint)bytesToWrite.Length, out ptrBytesWritten);
      bytesWritten = ptrBytesWritten.ToInt32();
   }

   public string PointerWrite(IntPtr MemoryAddress, byte[] bytesToWrite, int[] Offset, out int bytesWritten)
   {
      int iPointerCount = Offset.Length - 1;
      IntPtr ptrBytesWritten;
      bytesWritten = 0;
      byte[] buffer = new byte[4]; //DWORD to hold an Address
      int tempAddress = 0;

      if (iPointerCount == 0)
      {
         MemoryAPI.ReadProcessMemory(m_hProcess, MemoryAddress, buffer, 4, out ptrBytesWritten);
         tempAddress = Addr.ToDec(Addr.Make(buffer)) + Offset[0]; //Final Address
         MemoryAPI.WriteProcessMemory(m_hProcess, (IntPtr)tempAddress, bytesToWrite, (uint)bytesToWrite.Length, out ptrBytesWritten);

         bytesWritten = ptrBytesWritten.ToInt32();
         return Addr.ToHex(tempAddress);
      }

      for (int i = 0; i <= iPointerCount; i++)
      {
         if (i == iPointerCount)
         {
            MemoryAPI.ReadProcessMemory(m_hProcess, (IntPtr)tempAddress, buffer, 4, out ptrBytesWritten);
            tempAddress = Addr.ToDec(Addr.Make(buffer)) + Offset[i]; //Final Address
            MemoryAPI.WriteProcessMemory(m_hProcess, (IntPtr)tempAddress, bytesToWrite, (uint)bytesToWrite.Length, out ptrBytesWritten);

            bytesWritten = ptrBytesWritten.ToInt32();
            return Addr.ToHex(tempAddress);
         }
         else if (i == 0)
         {
            MemoryAPI.ReadProcessMemory(m_hProcess, MemoryAddress, buffer, 4, out ptrBytesWritten);
            tempAddress = Addr.ToDec(Addr.Make(buffer)) + Offset[i];
         }
         else
         {
            MemoryAPI.ReadProcessMemory(m_hProcess, (IntPtr)tempAddress, buffer, 4, out ptrBytesWritten);
            tempAddress = Addr.ToDec(Addr.Make(buffer)) + Offset[i];
         }
      }

      return Addr.ToHex(tempAddress);
   }

   public int PID()
   {
      return m_ReadProcess.Id;
   }

   public string BaseAddressH()
   {
      return Addr.ToHex(m_ReadProcess.MainModule.BaseAddress.ToInt32());
   }

   public int BaseAddressD()
   {
      return m_ReadProcess.MainModule.BaseAddress.ToInt32();
   }
}

public class Addr
{
   public static string Make(byte[] buffer)
   {
      string sTemp = "";

      for (int i = 0; i < buffer.Length; i++)
      {
         if (Convert.ToInt16(buffer[i]) < 10)
            sTemp = "0" + ToHex(buffer[i]) + sTemp;
         else
            sTemp = ToHex(buffer[i]) + sTemp;
      }

      return sTemp;
   }

   public static string ToHex(int Decimal)
   {
      return Decimal.ToString("X"); //Convert Decimal to Hexadecimal
   }

   public static int ToDec(string Hex)
   {
      return int.Parse(Hex, NumberStyles.HexNumber); //Convert Hexadecimal to Decimal
   }
}



Your code should now look like this (when the classes are minimized):



Alright, now that our memory functions are added to our project, it's time to make our button do something!

Add this code in the Form Class:
Code:
Memory oMemory = new Memory();




Add this code to the Event we described earlier:
Code:
Process[] aProcesses = Process.GetProcessesByName("Tutorial-i386"); //Find Tutorial-i386.exe
if (aProcesses.Length != 0) //If the process exists
{
      oMemory.ReadProcess = aProcesses[0]; //Sets the Process to Read/Write From/To
   oMemory.Open(); //Open Process

   int iStep2_Address = Addr.ToDec("57C310"); //The static address of the pointer (#1)
   int[] iStep2_Offsets = { 0x98, 0x4, 0x288, 0x24, 0x458 }; //Offsets from bottom to top (#2-#6)

   int bytesWritten; //Holds how many bytes were written by PointerWrite

   int iValue_To_Write = 1000; //Value that we want to write (Step2 requires that you change the value to 1000)
   byte[] bValue_To_Write = BitConverter.GetBytes(iValue_To_Write); //Turns 1000 into bytes

   string sWritten_Address = oMemory.PointerWrite((IntPtr)iStep2_Address, //PointerWrite starting with our Step2 Address
                     bValue_To_Write, //The value to write (as bytes)
                     iStep2_Offsets, //Our offsets
                     out bytesWritten); //Stores the # of Written Bytes

   if (bytesWritten == bValue_To_Write.Length) //If writing was successful
      MessageBox.Show("Wrote " + iValue_To_Write.ToString() + " to " + sWritten_Address + "!"); //Notify the user of success
   else
      MessageBox.Show("There was an error writing " + iValue_To_Write.ToString() + " to " + sWritten_Address + "."); //Notify the user of failure

   oMemory.CloseHandle(); //Close Memory Handle
}




If you found the pointer yourself, you will most likely have a different number of offsets as me, so change them accordingly. Otherwise, mine should work for you.

Unless I'm forgetting anything (which we will find out soon enough), Run your project, open Tutorial-i386.exe, and press Complete Step 2.
Now go to the Tutorial and see if Next is Enabled!

If not, make sure your Address and Offsets are correct. Check to see if the Address that the MessageBox shows is the same as in your Cheat Table ("P->XXXXXXXX")

This is how you determine iStep2_Address and iStep2_Offsets:


Note: These numbers (#1-#6) are referred to in the comments. #1 DOES NOT mean the first offset.

Now wait! What if you want to write somethine like a Float? Here's how:

Simply change:
Code:
byte[] bValue_To_Write = BitConverter.GetBytes(iValue_To_Write); //Turns 1000 into bytes

To:
Code:
byte[] bValue_To_Write = BitConverter.GetBytes((float)iValue_To_Write); //Turns 1000 into bytes


Here is a list of types. I can only confirm the usage of (float) and (double) as of now.

That concludes my tutorial! Please let me know if I forgot something, or if something is incorrect.
Also, feel free to ask questions, and I do take requests on trainers and possibly tutorials. It all depends!

Here is the whole project. It has a few more steps done so you can look at them!
Source Code - Steps 1,2,3,4,5,6,8


Last edited by KryziK on Wed Jan 26, 2011 7:58 pm; edited 1 time in total
Back to top
View user's profile Send private message
Post reviews:   Approve 2
Author Review
AhMunRa
Review: Approve
Post reference:
ReviewPosted: Mon Jan 17, 2011 9:16 am

Very concise yet explicit howto. Thank you.
Back to top
View user's profile Send private message
hitmetwice
Review: Approve
Post reference:
ReviewPosted: Sun Mar 24, 2013 6:36 am

good job! Smile
Back to top
View user's profile Send private message
Display:  
Cheat Engine Forum Index


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites