Как игровые тренеры меняют динамический адрес в памяти? - PullRequest
11 голосов
/ 28 мая 2010

Предположим, что я игра, и у меня есть глобальная int*, которая содержит мое здоровье. Работа тренера игры состоит в том, чтобы изменить это значение на что угодно, чтобы достичь режима бога. Я посмотрел учебники по игровым тренажерам, чтобы понять, как они работают, и основная идея заключается в том, чтобы использовать сканер памяти, чтобы попытаться найти адрес определенного значения. Затем измените этот адрес, введя DLL или что-то еще.

Но я сделал простую программу с глобальным int*, и ее адрес меняется каждый раз, когда я запускаю приложение, так что я не понимаю, как игровые тренеры могут жестко кодировать эти адреса? Или мой пример неверен?

Чего мне не хватает?

Ответы [ 5 ]

6 голосов
/ 28 мая 2010

Обычно это делается путем отслеживания цепочки указателей от статической переменной до адреса кучи, содержащей указанную переменную. Например:

struct CharacterStats
{
    int health;
    // ...
}

class Character
{
public:
    CharacterStats* stats;

    // ...

    void hit(int damage)
    {
        stats->health -= damage;
        if (stats->health <= 0)
            die();
    }
}


class Game
{
public:
    Character* main_character;
    vector<Character*> enemies;
    // ...
}

Game* game;

void main()
{
    game = new Game();
    game->main_character = new Character();
    game->main_character->stats = new CharacterStats;

    // ...

}

В этом случае, если вы последуете совету mikek3332002 и установите точку останова внутри функции Character :: hit () и не сможете вычитать, это приведет к тому, что все персонажи, включая врагов, будут неуязвимы. Решение состоит в том, чтобы найти адрес переменной «game» (которая должна находиться в сегменте данных или в стеке функции) и следовать всем указателям, пока не найдете адрес переменной здоровья.

Некоторые инструменты, например Cheat Engine, есть функция для автоматизации этого и попытаться найти цепочку указателей самостоятельно. Возможно, вам придется прибегнуть к обратному проектированию для более сложных случаев.

1 голос
/ 19 февраля 2015

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

С помощью перехвата API malloc (), free () и т. Д. Метод отличается от следующих указателей. Обнаружение начинается с записи всех динамических распределений памяти и параллельного поиска в памяти. Найденный адрес памяти кучи затем обратно сопоставляется с записанным распределением памяти. Вы узнаете размер объекта и смещение вашего значения в объекте. Вы повторяете это с помощью backtracing и получаете адрес возврата кода вызова malloc () или конструктора C ++. С помощью этой информации вы можете отслеживать и изменять все объекты, которые оттуда выделяются. Вы сбрасываете объекты, сравниваете их и находите гораздо более интересные значения. Например. Универсальный элитный игровой тренажер "Ugtrain" делает это так в Linux. Он использует LD_PRELOAD. Адаптация выполняется путем разборки на основе "objdump -D" и просто поиска вызова библиотечной функции с известным объемом памяти в ней.

См .: http://en.wikipedia.org/wiki/Trainer_%28games%29

Источник: Ugtrain: https://github.com/sriemer/ugtrain

Хук malloc () выглядит так:

static __thread bool no_hook = false;

void *malloc (size_t size)
{
    void *mem_addr;
    static void *(*orig_malloc)(size_t size) = NULL;

    /* handle malloc() recursion correctly */
    if (no_hook)
        return orig_malloc(size);

    /* get the libc malloc function */
    no_hook = true;
    if (!orig_malloc)
        *(void **) (&orig_malloc) = dlsym(RTLD_NEXT, "malloc");

    mem_addr = orig_malloc(size);

    /* real magic -> backtrace and send out spied information */
    postprocess_malloc(size, mem_addr);
    no_hook = false;

    return mem_addr;
}

Но если найденный адрес памяти находится внутри исполняемого файла или библиотеки в памяти, то, скорее всего, причиной динамического изменения является ASLR. В Linux библиотеки являются PIC (позиционно-независимым кодом), а в последних дистрибутивах все исполняемые файлы являются PIE (позиционно-независимыми исполняемыми файлами).

1 голос
/ 28 мая 2010

То, как вычислялись коды Gameshark, заключалось в том, чтобы выгрузить образ памяти приложения, затем сделать одну вещь, а затем посмотреть, что изменилось.Возможно, что-то изменится, но нужно искать шаблоны.Например, сбросить память, снять, сбросить память, снять снова, сбросить память, перезагрузить.Затем найдите изменения и получите представление о том, где и как хранятся боеприпасы.Для здоровья это будет похоже, но многое изменится (так как вы будете двигаться как минимум).Хотя это будет проще всего сделать при минимизации «внешних эффектов», например, не пытайтесь анализировать дампы памяти во время перестрелки, потому что многое происходит, выполняйте свои операции, стоя в лаве, или падая со здания,что-то в этом роде.

1 голос
/ 28 мая 2010

Пример для динамически распределенной переменной

Значение, которое я хочу найти, - это количество жизней, при котором мои жизни не уменьшаются до 0 и не заканчиваются.

  1. Сыграйте в игру и найдите в этом экземпляре местоположение переменной lifes.
  2. Найдя, используйте дизассемблер / отладчик, чтобы посмотреть, где в ней находятся изменения.
  3. Потерять жизнь.
  4. Отладчик должен был сообщить адрес, по которому произошло уменьшение.
  5. Заменить эту инструкцию на no-ops

Получил этот шаблон из программы tsearch


В результате исследования этой темы было найдено несколько похожих сайтов:

1 голос
/ 28 мая 2010

РЕДАКТИРОВАТЬ: не говоря уже о том, что это просто удача, однако последние 3 цифры указателя, кажется, остаются прежними. Возможно, это ASLR, включающий и изменяющий адрес базового образа или что-то в этом роде?

ааааааа, плохо, я использовал% d для printf для печати адреса, а не% p. После использования% p адрес остался прежним

#include <stdio.h>

int *something = NULL;

int main()
{
    something = new int;
    *something = 5;

    fprintf(stdout, "Address of something: %p\nValue of something: %d\nPointer Address of something: %p", &something, *something, something);
    getchar();
    return 0;
}
...