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

В настоящее время я редактирую игру под названием 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;
}

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

1 ответ

Решение

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

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

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

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

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

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

#define PTR_ADD( PTR, OFFSET ) \
   (((char *)(PTR)) + (OFFSET))

Поскольку вы разместили это, мы изменили наш 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, если ваш тип сборки соответствует архитектуре целевого процесса.

Другие вопросы по тегам