Каков самый чистый способ разыменования многоуровневых указателей со смещениями?

В настоящее время я редактирую память в игре под названием Assault Cube. К сожалению, из-за динамического выделения памяти адреса значений, которые я хочу редактировать, меняются каждый раз при запуске игры. К счастью, существуют статические указатели, которые всегда указывают на динамические адреса. Используя чит-движок, я могу найти указатели, но они иногда доходят до 8 уровней. Вместо того, чтобы каждый раз делать ********pointer, я бы предпочел делать: *pointer. Вдобавок ко всему, у них есть смещения, поэтому жестко закодировать их было бы кошмаром.

Вместо этого я использую эту функцию:

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;
}

но это очень грязно, и я привожу int* к int** и наоборот, что считается плохой практикой. Есть ли что-то, что я могу сделать, что не приведет к плохой практике? Я также нашел это в Интернете:

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;
}

что, я думаю, еще более уродливо, чем то, что я написал. Я не рекомендую вам читать это, если вы не хотите мигреней.


person user2327287    schedule 27.04.2013    source источник
comment
Учитывая требования, я не могу придумать ничего лучше. Единственное предложение, которое я бы сделал, это использовать указатели char* и char**. Тогда вам не понадобится /sizeof(int), и вы сможете корректно работать со смещениями, не кратными sizeof(int).   -  person john    schedule 28.04.2013
comment
Пожалуйста, объясните, почему это не является нарушением авторских прав, мошенничеством или иным образом неэтичным.   -  person brian beuning    schedule 28.04.2013
comment
@брайанбеунинг; Assaultcube имеет открытый исходный код, поэтому он, вероятно, не нарушает авторские права. Написание игровых читов — отличный способ узнать о моделях памяти, ассемблере и патчинге среди прочего.   -  person Rich    schedule 28.04.2013
comment
Рич попал в самую точку.   -  person user2327287    schedule 28.04.2013


Ответы (2)


Я не уверен в "самом чистом способе", но вы ничего не можете сделать, кроме как разыменовывать эти указатели. Я предлагаю использовать typedefs просто для того, чтобы сделать ваш код более читабельным.

Также не беспокойтесь о приведении int * к int **. Конечно, это считается «плохой практикой», но если вы знаете, что делаете, это может быть именно то, что требуется. Вы просто должны быть осторожны.

typedef int *** intPtr3;
typedef int ****** intPtr6;

Вы также можете использовать некоторые макросы для очистки синтаксиса. Это был бы хороший пример разумного использования макроса для улучшения читабельности и чистоты, а также снижения вероятности ошибок:

#define DEREF6( PTR ) \
  ******(PTR)

Наконец, есть хороший макрос, который я использую довольно часто для перемещения указателя на количество байтов в памяти:

#define PTR_ADD( PTR, OFFSET ) \
   (((char *)(PTR)) + (OFFSET))
person RandyGaul    schedule 27.04.2013
comment
+1 Хороший ответ, он показывает один из немногих случаев, когда макросы и определения типов используются хорошо. - person GRAYgoose124; 28.04.2013
comment
См.: Хорошая ли идея typedef указатели?. - person David C. Rankin; 02.01.2020

Поскольку вы опубликовали это, мы изменили наш FindDMAAddy, чтобы он был чище, и теперь он выглядит так:

uintptr_t FindDMAAddy(HANDLE hProc, uintptr_t ptr, std::vector<unsigned int> offsets)
{
    for (unsigned int i = 0; i < offsets.size(); ++i)
    {
        ReadProcessMemory(hProc, (BYTE*)ptr, &ptr, sizeof(ptr), 0);
        ptr += offsets[i];
    }
    return ptr;
}

Несколько строк кода, нет необходимости определять длину вручную и совместимость с x64, если тип сборки соответствует архитектуре целевого процесса.

person GuidedHacking    schedule 02.01.2020