Каков самый чистый способ разыменования многоуровневых указателей со смещениями?
В настоящее время я редактирую игру под названием 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, если ваш тип сборки соответствует архитектуре целевого процесса.