Search code examples
cpointersreverse-engineeringmemory-editing

What is the cleanest way to dereference multi-level pointers with offsets?


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.


Solution

  • 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))