C# Multilevel pointers wrong last calc - c#

I am currently trying to implement memory read in C# on base pointers found using Cheat Engine. I am 100% sure I have found the right pointers and offsets since they work just fine in Cheat-Engine, even between restarts.
I am now implementing it in C#, have not had any issues with single level points, but for some reason I can't get my last multilevel pointer to work.
It all goes well until the last value it has to add and then it returns something "random" to me, these are the pointers I have found and I can see it works in Cheat Engine.
This is my implementation in C#:
public static int ReadFromPointer(int address, int[] offsets)
{
Console.WriteLine("----------");
Console.WriteLine("Address: " + address);
int ptr = ReadPointer(address);
Console.WriteLine($"Pointer returned as int: {ptr}, hex: {ptr:X}");
foreach (var offset in offsets)
{
Console.WriteLine($"Adding offset: {offset:X} to Pointer: {ptr:X}");
ptr = ReadPointer(ptr + offset);
Console.WriteLine($"Pointer returned as int: {ptr}, hex: {ptr:X}");
}
Console.WriteLine("----------");
return ptr;
}
private static int ReadPointer(int adress)
{
int ptrNext;
int bytesRead = 0;
byte[] _Value = new byte[4];
ReadProcessMemory((IntPtr)ProcessHandle, (IntPtr)adress, _Value, IntPtr.Size, ref bytesRead);
ptrNext = BitConverter.ToInt32(_Value, 0);
return ptrNext;
}
and I call it using the following:
var valueToFind = ProcessHelper.ReadFromPointer((int)baseAddress + 0x00C45A5C, new []{ 0xEC, 0x1C, 0x178, 0x74, 0x458 });
Now here comes the "random" part, every pointer is added correctly except the last when it has to add 0x458 to pointer 1E138F80, this should return 1E1393D8, but ends up returning "41C00000"
I am unsure if it's due to my last pointer no longer being 4 bytes or if a conversion is somehow happening that mixes it up.
Any help here would be greatly appreciated!

Your problem is, after you add the last offset you are de-referencing it again as if this address points to the variable you are trying to get the address of. In fact, at this point the address is the address of your variable, not a pointer to it. When the last offset is added, you should stop and print that variable instead.
I am pretty sure that changing:
foreach (var offset in offsets)
to
for (var i = 0; i < offsets.Length; ++i)
and changing
ptr = ReadPointer(ptr + offset);
to
ptr = ReadPointer(ptr + offset[i]);
will fix it.
Print after this for loop concludes and, it should print the correct address.
If that fails, I have prepared a replacement function for you:
public static IntPtr FindDMAAddy(IntPtr hProc, IntPtr ptr, int[] offsets)
{
var buffer = new byte[IntPtr.Size];
foreach (int i in offsets)
{
ReadProcessMemory(hProc, ptr, buffer, buffer.Length, out var read);
ptr = (IntPtr.Size == 4)
? IntPtr.Add(new IntPtr(BitConverter.ToInt32(buffer, 0)), i)
: ptr = IntPtr.Add(new IntPtr(BitConverter.ToInt64(buffer, 0)), i);
}
return ptr;
}

Related

Unsafe conversion to given pointer in buffer

I am trying to figure out how to get this method to start at a index being ptr instead of overwriting from 0 - 8 bytes. Also if anyone knows how to make this work with Big / Little edian machines due to being networked, that would be awesome.
private byte[] buffer = new byte[30];
private int ptr = 0;
unsafe void GetBytes(ulong value)
{
fixed (byte* b = buffer) //start at buffer[ptr]
*((int*)b) = *(int*)&value;
ptr += 8;
}
I figured out how to make a pointer to the ptr via the following
private byte[] buffer = new byte[30];
private int ptr = 0;
unsafe void GetBytes(ulong value)
{
fixed (byte* b = &buffer[ptr]) //start at buffer[ptr]
*((int*)b) = *(int*)&value;
ptr += 8;
}
You can convert endianness easily:
using System.Buffers.Binary;
if (!BitConverter.IsLittleEndian)
{
value = BinaryPrimitives.ReverseEndianness(value);
}
I would prefer to use Span which will be safer and easier to read:
using System.Buffers.Binary;
Span<byte> span = buffer;
BinaryPrimitives.WriteUInt64LittleEndian(span.Slice(ptr), value);
If you want to use your system's endianness rather than something explicit, you can do that too:
using System.Runtime.InteropServices;
Span<byte> span = buffer;
MemoryMarshal.Write(span.Slice(ptr), value);
Edit for non-Core:
You can use HostToNetworkOrder to convert integers to big endian prior to writing to your pointer.

Marshalling part of a struct

I've been trying to figure out whether it is possible to marshal a part of a struct into an array of bytes without any errors. The reason for this is because the part not going to be marshalled has a variable sized array. This is the the code I'm using.
C#:
public struct Random {
[MarshalAs(UnmanagedType.I4)]
public int a;
[MarshalAs(UnmanagedType.I4)]
public int b;
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_RECORD)]
public Random1[] r;
}
public struct Random1 {
[MarshalAs(UnmanagedType.I4)]
public int c;
}
private void Form1_Load(object sender, EventArgs e) {
int size = Marshal.SizeOf(typeof(Random));
int s = 4;
Random r = new Random();
Random r1 = new Random();
r.a = 1;
r.b = 5;
r.r = new Random1[2];
r.r[0].c = 10;
r.r[1].c = 12;
IntPtr p = Marshal.AllocHGlobal(size);
try {
byte[] arr = new byte[size];
Marshal.StructureToPtr(r, p, false);
Marshal.Copy(p, arr, 0, 8);
Marshal.FreeHGlobal(p);
p = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, p, 8);
r1 = (Random) Marshal.PtrToStructure(p, typeof(Random));
Marshal.FreeHGlobal(p);
p = IntPtr.Zero;
Debug.WriteLine(r1.a);
Debug.WriteLine(r1.b);
Debug.WriteLine(r1.r[0].c);
Debug.WriteLine(r1.r[1].c);
} finally {
if (p != IntPtr.Zero) {
Marshal.FreeHGlobal(p);
}
}
}
When I try this code it gives me an ArgumentException in the StructureToPtr. What am I doing wrong or can this be done? If not I've read something about using IntPtr. Could someone tell me how to do this?
I've been thinking long and hard about this one, and after trying several (very hackish) options, I have arrived at the conclusion that what you are doing is not possible.
First of all, it isn't possible to directly marshal an array of variable length, because no meaningful size offset can be computed by the Marshal, and therefore the memory layout can not be determined.
If the struct wouldn't contain any non-blittable, non-primitive types, you could theoretically do something like this:
byte[] buffer = new byte[8];
Marshal.Copy(new IntPtr(&r), buffer, 0, 8);
IntegersOnlyStruct partialStruct;
fixed (byte* b = buffer)
partialStruct = *(IntegersOnlyStruct*) b;
And obtain the two integers contained in the struct that way. However, since your Random1 is a non-primitive, non-blittable type this approach won't get you very far either.
The IntPtr approach to keep a pointer to a memory location where your Random1 struct is kept in memory is an alternative that could possibly work, but I haven't gone in-depth on that, yet.
I think you may want to re-think the way you approach this, as it's not possible in its current form. If anybody can prove otherwise, I'd be happy to see how it can be done.

Searching Memory For Specific Values C# (Xbox 360)

EDIT: I think I have an idea of a possible solution for the actual searching of values. By making sure the user input ends in 0 the issue should be resolved. This would involve subtracting the last digit from the uint (which I do not know how to get, unless I go the convert to string, trim end back to uint method which is ugly but I guess it could work) and then subtracting it. If anyone has any tips on how to do this please help me out!
I've been working on a program to search memory on the Xbox 360 for specific values, if you are familiar, it is similar to "Cheat Engine". I've gotten the basics down, but I just ran into an issue. My method to search memory is dependent on starting your search at an address that will line up with your value. If that doesn't make sense to you here is the code:
private void searchInt32(int Value, uint Address, uint BytesToSearch)
{
for (uint i = 0; i <= BytesToSearch; i+=4)
{
int recoveredMem = XboxSupport.littleEndtoInt(XboxSupport.GetMem(Address + i, 4), 0);
//Recover Memory (As Bytes) and convert to integer from address (incremented based on for loop)
if (recoveredMem == Value) //Check if recovered mem = search value
{
writeToFile(Address + i, Convert.ToString(Value)); //If recovered mem = search value, write to a text file
}
siStatus.Caption = String.Format("Searching Bytes {0} out of {1}...", i, BytesToSearch); //Update status caption
}
}
As you can see, the code is kept to a minimum and it's also about as fast as possible when it comes to recovering memory from a console. But, if the 4 bytes it recovers don't line up with the value, it will never return what you want. That's obviously a serious issue because the user won't know where their value is or what address to start at to return the correct value. I then attempted to use the following code to fix the issue:
private void searchUInt32(uint Value, uint Address, uint BytesToSearch)
{
siStatus.Caption = String.Format("Recovering Memory...");
byte[] data = XboxSupport.GetMem(Address, BytesToSearch); //Dump Console Memory
FileStream output = new FileStream("SearchData.dat", FileMode.Create);
BinaryWriter writer = new BinaryWriter(output);
writer.Write(data); //Write dump to file
writer.Close();
output = new FileStream("SearchData.dat", FileMode.Open);
BinaryReader reader = new BinaryReader(output); //Open dumped file
for (uint i = 0; i *4 < reader.BaseStream.Length; i++)
{
byte[] bytes = reader.ReadBytes(4); //Read the 4 bytes
Array.Reverse(bytes);
uint currentValue = BitConverter.ToUInt32(bytes, 0); //Convert to UInt
if(currentValue == Value) //Compare
writeToFile(Address + i * 4, Convert.ToString(Value));
siStatus.Caption = String.Format("Searching Bytes {0} out of {1}...", i * 4, BytesToSearch);
}
reader.Close();
File.Delete("SearchData.dat");
}
There is a lot more code, but essentially it does the same thing, just using a file. My original goal was to have users be able to input their own memory blocks to be searched, but right now it seems that just won't work. I do not really want to have the program search all of the memory because that might end up being a slow process (depending on the size of the process being dumped) and often times the values being looked for can be narrowed down to areas of writeable code, removing junk addresses from the executable portion of the process. I am just looking to see if anyone has any suggestions, I was thinking I could possibly get the entry address from the process (I have a function for it) and using a little math correct user input addresses to work properly but I wasn't entirely sure how to do it. If anyone has any suggestions or solutions I'd appreciate any help I can get. If any of my post needs to be clarified/cleaned up please let me know, I'll be glad to do anything that might help me to an answer.
Thanks!
Edit: Temporary (hopefully) Solution:
When I load addresses into the tool they are loaded as strings from a text file, then a conversion to uint is attempted. I solved the not even issue using the following code:
sA[0] = sA[0].Remove(sA[0].Length - 1) + "0"; //Remove last character and replace w/ 0
//Add 16 to the search length
Instead of dumping memory to disk and reading every iteration, scan the target process' memory in chunks, and then marshal the data to leverage the efficiency of pointer arithmetic.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace MemoryScan {
internal class Program {
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead);
private static unsafe void Main(string[] args) {
Process process = Process.GetProcessesByName("notepad")[0]; //example target process
int search = 100; //search value
int segment = 0x10000; //avoid the large object heap (> 84k)
int range = 0x7FFFFFFF - segment; ; //32-bit example
int bytesRead;
List<int> addresses = new List<int>();
DateTime start = DateTime.Now;
for (int i = 0; i < range; i += segment) {
byte[] buffer = new byte[segment];
if (!ReadProcessMemory(process.Handle, new IntPtr(i), buffer, segment, out bytesRead)) {
continue;
}
IntPtr data = Marshal.AllocHGlobal(bytesRead);
Marshal.Copy(buffer, 0, data, bytesRead);
for (int j = 0; j < bytesRead; j++) {
int current = *(int*)(data + j);
if (current == search) {
addresses.Add(i + j);
}
}
Marshal.FreeHGlobal(data);
}
Console.WriteLine("Duration: {0} seconds", (DateTime.Now - start).TotalSeconds);
Console.WriteLine("Found: {0}", addresses.Count);
Console.ReadLine();
}
}
}
Test Results
Duration: 1.142 seconds
Found: 3204
Create a generic class to make type marshaling easier, like so:
public static class MarshalHelper
{
public unsafe static T Read<T>(IntPtr address)
{
object value;
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Int16:
value = *(short*)address;
break;
case TypeCode.Int32:
value = *(int*)address;
break;
case TypeCode.Int64:
value = *(long*)address;
break;
default:
throw new ArgumentOutOfRangeException();
}
return (T)value;
}
}

Convert a struct to bytes, save to file and read back to struct by another application in C#

I tried writing structures to a binary file but I can't read them correctly.
This is my struct. It has a dynamic number of "values". If the number of values is 3, then GetSize() will return 8 + (8*3) = 32
[StructLayout (LayoutKind.Sequential)]
public struct Sample
{
public long timestamp;
public double[] values;
public int GetSize()
{
return sizeof(long) + sizeof(double) * values.Length;
}
}
First, I convert the structure to bytes by:
public static byte[] SampleToBytes(Sample samp)
{
int size = samp.GetSize();
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(samp, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
Then, I write the bytes using BinaryWriter, and exit.
When I have to run the program again and read the file I saved. I use BinaryReader. I get every 32 bytes from the file and convert each array of 32 bytes back to struct using:
public static Sample BytesToSample(byte[] arr)
{
Sample samp = new Sample();
IntPtr ptr = Marshal.AllocHGlobal(32);
Marshal.Copy(arr, 0, ptr, 32);
samp = (Sample)Marshal.PtrToStructure(ptr, samp.GetType());
Marshal.FreeHGlobal(ptr);
return samp;
}
However, a SafeArrayTypeMismatchException occurs at PtrToStructure().
Could anyone please tell me what I am doing wrong?
Thanks.
You do realize that double[], being an array type, is a reference type? The struct holds the long followed by a reference to the heap object.
That would be the reason why it doesn't work, I think.
I believe you should simply write the elements of your array to a binary writer.

C# pointer offset > 255 - ProcessMemoryReader

I know there are many tutorials out there showing you how to use the "ProcessMemoryReader" functions. But this problems seems to be unique or not solved yet.
For quite a while I've been digging into other people's code to find a way to use multiple offsets.
And I thought that using multiple offsets was the problem for me, but I think it's a problem with the fact that my offset value is bigger than 255.
The game I'm trying to get the memory values from is called "Assault Cube".
As I wasn't sure whether I got the right offset values I googled what others results where.
They seem to be exactly the same:
http://cheatengine.org/tables/moreinfo.php?tid=1142 (You can view the .ct file with notepad if you don't have cheat engine installed.)
Here is my code, using the ProcessMemoryReader.cs.
private void timer1_Tick(object sender, EventArgs e)
{
int bytesread;
int pointerbase;
byte[] memory;
Process[] myprocess = Process.GetProcessesByName("ac_client");
if (myprocess.Length != 0)
{
preader.ReadProcess = myprocess[0];
preader.OpenProcess();
//Ammo
memory = preader.ReadProcessMemory((IntPtr)0x4DF73C, 4, out bytesread);
pointerbase = BitConverter.ToInt32(memory, 0);
pointerbase += 0x00; //0 // 14 // 378
byte[] memory1 = preader.ReadProcessMemory((IntPtr)pointerbase, 4, out bytesread);
int pointerbase1 = BitConverter.ToInt32(memory1, 0);
pointerbase1 += 0x14; //0 // 14 // 378
byte[] memory2 = preader.ReadProcessMemory((IntPtr)pointerbase1, 4, out bytesread);
int pointerbase2 = BitConverter.ToInt32(memory2, 0);
pointerbase2 += 0x378; //00 // 14 // 378
byte[] memory3 = preader.ReadProcessMemory((IntPtr)pointerbase2, 4, out bytesread);
int valueis = BitConverter.ToInt32(memory3, 0);
label1.Text = valueis.ToString();
}
Though with a single pointer the process works fine, for example:
//HP
memory = preader.ReadProcessMemory((IntPtr)0x4DF73C, 4, out bytesread);
pointerbase = BitConverter.ToInt32(memory, 0);
pointerbase += 0xf4;
byte[] memory1 = preader.ReadProcessMemory((IntPtr)pointerbase, 4, out bytesread);
int valueis = BitConverter.ToInt32(memory1, 0);
label2.Text = valueis.ToString();
So that works, it's pretty straight forward what's happening here, but I can't figure how to read the Ammo code with the multiple offsets.
I'm not familiar with CheatEngine and it's table format, but I do not get the impression it's pointing to the memory addresses that you are using.
You read 4 bytes at 0x4DF73C, which is used as the new memory address for the next read. This is repeated a few times. Basically, you're reading information from a pointer to a pointer to a pointer. Are you sure this is what is intended?
There's no reason whatsoever that an offset value greater than 255 would be a problem.
Use FindDMAAddy to walk the pointer chain for you, here is a working example, make sure you run as admin:
public static IntPtr FindDMAAddy(IntPtr hProc, IntPtr ptr, int[] offsets)
{
var buffer = new byte[IntPtr.Size];
foreach (int i in offsets)
{
ReadProcessMemory(hProc, ptr, buffer, buffer.Length, out
var read);
ptr = (IntPtr.Size == 4) ? IntPtr.Add(new IntPtr(BitConverter.ToInt32(buffer, 0)), i) : ptr = IntPtr.Add(new IntPtr(BitConverter.ToInt64(buffer, 0)), i);
}
return ptr;
}
var modBase = GetModuleBaseAddress(proc.Id, "ac_client.exe");
var ammoAddr = FindDMAAddy(hProc, (IntPtr)(modBase + 0x10f4f4), new int[] { 0x374, 0x14, 0 });
Console.WriteLine("Ammo address " + "0x" + ammoAddr.ToString("X"));
int newAmmo = 1337;
byte[] buffer = new byte[4];
ReadProcessMemory(proc.Handle, ammoAddr, buffer, 4, out _);
Console.WriteLine("Ammo value " + BitConverter.ToInt32(buffer, 0).ToString());
WriteProcessMemory(hProc, ammoAddr, newAmmo, 4, out _);

Categories

Resources