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.
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:
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:
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:
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:
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:
Add this code to the Event we described earlier:
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:
Change to
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
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
Change 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 Cuup|ErcHimA| on 28th May 2014, 9:58 am; edited 1 time in total