I'm currently memory editing a game called Assault Cube. Unfortunately, because of dynamic memory allocation, the addresses to the values that I want to edit change each time the game starts. Fortunately, there are static pointers that always point to the dynamic addresses. Using cheat engine, I can find the pointers, but they sometimes go up to 8 levels. Instead of doing ********pointer
each time, I would much rather do: *pointer
. On top of that, they have offsets, so it would be a nightmare to hard-code them.
Instead, I am using this function:
int* getLowestPointer(int** highestPointer, int levels, int offsets[])
{
for (int i = 0; i < levels; i++) {
highestPointer = (int**) (*highestPointer + offsets[i]/sizeof(int)); // I am dividing by sizeof(int) here to undo pointer arithmetic (since the offsets are the difference between the offsetted pointer and the base pointer - not in integer increments)
}
return (int*) highestPointer;
}
but it is very messy, and I am casting int* to int** and vice-versa which is considered bad practice. Is there something that I can do which doesn't result in bad practice? I also found this on the web:
DWORD FindDmaAddy(int PointerLevel, DWORD Offsets[], DWORD BaseAddress)
{
DWORD Ptr = *(DWORD*)(BaseAddress);
if(Ptr == 0) return NULL;
for(int i = 0; i < PointerLevel; i ++)
{
if(i == PointerLevel-1)
{
Ptr = (DWORD)(Ptr+Offsets[i]);
if(Ptr == 0) return NULL;
return Ptr;
}
else
{
Ptr = *(DWORD*)(Ptr+Offsets[i]);
if(Ptr == 0) return NULL;
}
}
return Ptr;
}
which I think is even uglier than what I wrote. I don't recommend you read it unless you want migraines.
I'm not sure about "cleanest way", but there's nothing you can do except to dereference those pointers. I suggest using typedefs
just to make your code more readable.
Also don't worry about casting int *
to int **
. Sure it's considered "bad practice", but if you know what you're doing it might be exactly what is required. You just have to be careful.
typedef int *** intPtr3;
typedef int ****** intPtr6;
You can also use some macros to clean up your syntax. This would be a good example of intelligently using a macro to benefit readability and cleanliness, and reduce chance of errors:
#define DEREF6( PTR ) \
******(PTR)
Lastly there's a nice macro I use quite often for moving a pointer an amount of bytes in memory:
#define PTR_ADD( PTR, OFFSET ) \
(((char *)(PTR)) + (OFFSET))