Как найти смещение cheat engine

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



Для чего нужен поиск указателей

Переменные объекта в игре создаются динамически, чтобы не нагружать оперативную память игры и процесса – с текущими параметрами игр оперативной памяти не хватит. Соответственно, ведется учет, где в памяти размещается тот или иной параметр. Базовый элемент — указатель, а внутри – параметры здоровья, ресурсов, опыта, патронов, денег. При каждом новом запуске игры или нового уровня, адреса динамических игровых параметров переезжают в другую область памяти. Для изменения приходится снова и снова находить. Для того, что бы этого не делать применяется поиск указателей.

Найти одноуровневый указатель

Запускаем Cheat Engine. Находим и копируем в нижнее окно адрес переменной, которая отвечает за нужный параметр. Правым кликом по адресной строке вызываем меню, находим строку «Find out what writes to this address». Ставим break на запись и разрешаем запуск отладчика. Идем в игру и тратим часть золота или теряем одну жизнь — чтобы изменить показатель. Возвращаемся к Cheat Engine и видим в окне отладчика новые строки. Выбираем одну типа mov и переходим во вкладку «More information». Правым кликом открываем меню и выбираем «Copy info to clipboard» — скопированное переносим в блокнот, закрываем отладчик.

Найти одноуровневый указатель в Cheat Engine

Далее переходим в главное окно программы и в поисковой строке вводим адрес из указанной области 07AF.., отмечаем галочкой НЕХ и тип значения 4Б, — запускаем поиск. В результатах поиска ищем постоянный адрес – выделяется зеленым. Копируем в нижнее окно и кликаем дважды по строке «Adress».

Как найти указатель в Чит Энджин

Копируем адрес сверху, отмечаем галочкой «Pointer» и вставляем в нижнее выпавшее поле. Тип определяем исходный. Далее при помощи вендового калькулятора рассчитываем смещение между первоначальным адресом, копированным в блокнот и найденным зеленым. Результат вставляем во второе поле снизу и жмем «Ок». После этого правым кликом по значению – «Value» выбираем в меню «Show as decimal» — отражать показатели в десятичном формате. Итог сохраняем в типе файла *.СТ. При загрузке этого файла в Cheat Engine с запуском уровня не надо будет снова искать переменные.

Найти одноуровневый указатель в Чит Энджин

Найти многоуровневый указатель

Многоуровневый – это такой, который ссылается не на искомую информацию, а на другой указатель. Таких уровней может найтись сколько угодно. Многоуровневая адресация усложняет процесс поиска цепочки указателей. Обработка занимает время. Сканирование памяти проводится 8-12 раз с перезапуском игры до тех пор, пока не выявится постоянный результат и один показатель не отразит хоть раз одинаковый результат с игровым параметром при перезагрузке.


  1. 2010-07-06, 02:42 AM


    #1

    Пошаговое руководство по определению базового адреса и смещений для игровых величин (HP, MP и т.д.).

    В целом, процедура абсолютно одинакова для большинства программ, посему, раз прочитав подобное руководство для другой игры, которых хватает на нашем ресурсе, читать уже для Jade Dynasty особого смысла нет. Хотя, здесь я приведу несколько дополнительных вещей и пояснений.

    Note:
    – здесь не дается определения базовому адресу, смещениям и т.п.
    – здесь нет детального описания функций и окон CE
    – здесь нет детального описания ассемблерных команд
    – данное руководство на 90% применимо и для Perfect World’а, т.к. движки у этих двух игр одинаковые.
    – перед многими значениями стоит символ «$». Используется для идентификации шестнадцатеричных величин

    Приступим.
    Загружаем клиент Jade Dynasty, запускаем Cheat Engine (CE) и присоединяем последний к процессу игры «elementclient.exe». Объектом поиска будет самая доступная величина – количество жизней (HP), в моем случае, равная 779 единиц.
    В поле «Value» CE вводим значение 779, выбираем тип сканирования «Exact value» и тип величины «4 bytes», и начинаем сканирование «first scan»

    По завершению первого сканирования, CE отобразит список адресов (если он не очень велик), значения которых в момент поиска равнялись 779. Таких адресов может быть 10, может быть и сотни тысяч. Поэтому, нам требуется точно определить нужный адрес. Возвращаемся в игру и любым способом меняем значение HP, например, снимем с чара какую-либо вещь, дающую прирост HP:

    Как только значение изменится, продолжаем поиск в CE, при этом тип сканирования оставляя «Exact value»

    Найдено 4 адреса. Можно еще раз изменить значение HP и посмотреть, как будут меняться значения у этих адресов.

    Через несколько секунд становится ясно: два первых адреса не имеют отношения к искомой величине, а два последних – текущее значение HP и максимальное.

    Добавляем оба адреса в список и, выбрав «HP max», вызываем контекстное меню.

    Нам требуется отыскать участок кода, который записывает значения в выбранный адрес $1265F098: «Find out what writes to this address»

    Выполнив указанную команду, CE отобразит окно со списком опкодов (operation code, код операции)

    Код операции, операционный код, опкод — часть машинного языка, называемой инструкцией, определяющей операцию, которая должна быть выполнена.

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

    Посмотрим дополнительную информацию для инструкции по адресу «$0046b5ca»:

    CE отобразит окно, где будут указаны значения регистров и дизассемблированный участок кода клиента; инструкция mov [esi + $288], ecx как раз и отвечает за запись величины HP по адресу $1265F098, а значение указателя равно $1265EE10.
    Смещение: +288.
    $1265EE10 – следующая величина для поиска.

    Note: если не будет найдено ни одного значения, значит требуется повторить процедуру по определению значения указателя (просмотр списка опкодов)

    Берем первый из найденных адресов (совсем не обязательно, что данный адрес даст какой-либо результат), добавляем его в список (название +$288 для более легкой идентификации смещений) и теперь начнем поиск того, что обращается к данному адресу

    Список опкодов достаточно велик, что, по большей части, свидетельствует о правильности поиска.

    Рассмотрим две инструкции.
    первая по адресу: $004c2ad2

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

    вторая по адресу: $004d11c8

    Человек, знакомый с ассемблером, сразу видит результат… К этому результату вернемся чуть позже, т.к. завершим процедуру поиска базового адреса по выработанному алгоритму.
    $039F6E20 – следующая величина для поиска.

    Осуществив сканирование, CE выдаст список и выделит первый адрес зеленым цветом, что означает «статический адрес»
    Это и есть «почти» базовый адрес, которым пользуются большинство ботоделов. Используя его, и указанные ранее смещения, можем записать:
    [$00B06B5C] + $28 + $288 – Максимальное значение HP
    Однако (возвращаясь к ассемблеру), хоть мы и нашли статический адрес, но этот адрес является адресом «структуры игрока», в то время как истинный базовый адрес – $00B03064

    mov eax, [00B03064]
    mov eax, [eax+1c]
    mov eax, [ecx+28]

    Итог:
    [Базовый адрес] = $00B03064
    [BA] + $1C + $28 + $288 – Максимальное значение HP

    Note: Текущее значение HP искать нет уже необходимости, т.к. адрес был найден на первых шагах и он отличается от HP max на 18 байт, т.е.:
    [BA] + $1C + $28 + $270 – Текущее значение HP


    (c) Dwar
    копирование материала разрешено только с указанием автора и адреса источника

    Please, post your questions on forum, not by PM or mail

    I spend my time, so please pay a little bit of your time to keep world in equilibrium


  2. The Following 2 Users Say Thank You to Dwar For This Useful Post:



  3. 2010-07-17, 07:19 PM


    #2

    re: Поиск базового адреса, смещения. Jade Dynasty

    Quote Originally Posted by Dwar

    Note: Текущее значение HP искать нет уже необходимости, т.к. адрес был найден на первых шагах и он отличается от HP max на 8 байт, т.е.:
    [BA] + $1C + $28 + $280 – Текущее значение HP

    отличается не на 8 а на 18 байт
    [BA] + $1C + $28 + $270 – Текущее значение HP
    За гайд спс



  4. 2010-07-18, 01:26 AM


    #3

    re: Поиск базового адреса, смещения. Jade Dynasty

    точно . подправил

    Please, post your questions on forum, not by PM or mail

    I spend my time, so please pay a little bit of your time to keep world in equilibrium



  5. 2010-11-29, 03:58 PM


    #4

    lirikmel is offline


    New member




    Rep Power
    0

    re: Поиск базового адреса, смещения. Jade Dynasty

    спасибо кое что новое узнал….если же у кого то знание ассемблера и команд на уровне табуретки (вопщем как у меня)я думаю вам поначалу будет проще использовать Artmoney (качаете последнюю версию ,открываете хелп и переходите к пункту 10 все подробно описано )



  6. 2012-01-26, 11:55 AM


    #5

    godie1 is offline


    Guest




    Rep Power
    0

    Катит только для jd на forsaken world не идет .



  7. 2012-03-28, 04:51 AM


    #6

    FullSilence is offline


    Guest




    Rep Power
    0

    А как быть если “js EXECryptor_RegConst_7+927C4”?
    Попробовал “Unpacker ExeCryptor” говорит не упакован.

    Это не из Jade, но всё же.


Completing the CAPTCHA proves you are a human and gives you temporary access to the web property.

What can I do to prevent this in the future?

If you are on a personal connection, like at home, you can run an anti-virus scan on your device to make sure it is not infected with malware.

If you are at an office or shared network, you can ask the network administrator to run a scan across the network looking for misconfigured or infected devices.

Another way to prevent getting this page in the future is to use Privacy Pass. Check out the browser extension in the Chrome Web Store.

Cloudflare Ray ID: 72104f8f0acb719e • Your IP : 109.70.150.94 • Performance & security by Cloudflare

Читерство

Навеяно недавней дискуссией про игры с донатом. Собственно, в нормальных, сингл играх микротранзакции уже не новость. Тот же Dead Space 3, говорят, еще в свежих Assassin Creed появилось. Логично ожидать, что это безобразие продолжится и таких игр будет появляться все больше. Следовательно, вопрос читерства становится все более актуальным. Мы же не хотим донатить и тем самым поощрять скотскую тенденцию.

Не, разумеется, можно играть честно и набирать нужные ресурсы в самой игре. Но разработчики не дураки, и чтобы стимулировать геймеров к донату, они, можно не сомневаться, позаботились о том, чтобы честная игра была некомфортной, и нужное количество ресурсов или опыта можно было добыть только путем страданий. Да даже если и достаточно быстро набирается честно — один черт, если бы я хотел играть в тупой фарм одних и тех же локаций, я бы давно играл в какую-нибудь ММО, которые все без исключения про тупой фарм одних и тех же локаций. Так что будем читерить.

Собственно, о читерстве речи уже и нет. Мы используем возможность, предусмотренную в игре самими разработчиками как часть игрового процесса. Т.е. все честно. То, что они сделали ее платной — да пошли они в жопу.

Для лечения игры будем использовать Cheat Engine. Есть еще аналогичная по возможностям софтина — Artmoney, но я ее не знаю. Вообще, с помощью CE начитерить себе нужное количество денег, опыта, ресурсов, да и вообще чего угодно — не представляет ни малейшей проблемы. Но есть одно неудобство: при каждом запуске игры (а в некоторых играх — и при каждой загрузке локации) нужные переменные приходится искать заново.

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

Как водится, сколько-нибудь толковый гайд гуглом найти проблематично. Даже тот, по которому разбирался я — давно сгинул, и сайт тоже. Так что, пока что-то еще помню, решил записать здесь. Чтобы, если когда-нибудь понадобится, не искать и не напрягать память.

1. Для начала находим адрес переменной, отвечающей за нужный нам игровой параметр. Подробно про этот шаг писать смысла нет, он общеизвестен. Копируем адрес в нижнее окно.

2. В нижнем окне кликаем правой кнопкой по строке адреса и в появившемся меню выбираем “Find out what writes to this address” — ставим бряк на запись. Даем разрешение на запуск дебаггера.

3. Возвращаемся в игру и проделываем там что-нибудь, что изменит нужное нам значение. Покупаем/продаем, если ищем деньги, или подставляемся под пулю, если здоровье.

4. Переключаемся на CE и в окне дебаггера видим появившуюся запись из одной или нескольких строк. Выбираем одну (вида mov [eax+xxx],ecx) и нажимаем сбоку кнопку “More information”.

5. Кликаем правой кнопкой по списку адресов вверху и нажимаем “copy info to clipboard”. Копируем куда-нибудь в блокнот. После этого закрываем окно информации и в окне дебаггера нажимаем кнопку “Stop”, чтобы не гонять лишние процессы. Закрываем дебаггер.

Картинка №1.
[​IMG]

6. В основном окне CE в окно для поиска вводим адрес, который видели в указанном на картинке №1 месте. Ставим рядом галку Hex, тип значения — 4 байта. Нажимаем поиск. Листаем появившийся список адресов и находим первый, отмеченный зеленым — статический адрес. Копируем его вниз. Если на данном этапе зеленых адресов не нашлось — значит, с этой игрой не все так просто, и чтобы создать чит, понадобится скрипт на языке ассемблера. Или пробуем способ, описанный во второй части заметки.

7. В нижнем окне дважды кликаем по адресу (именно там, где в заголовке таблицы указано “Adress”).

Картинка №2.
[​IMG]

8. Копируем из верхней строки адрес вида “program.exe+HEX”.

9. Ставим галку “Pointer”. Ниже появляется два новых поля, верхнее, где был адрес, становится недоступным. В самое нижнее поле вставляем адрес, скопированный сверху. Тип выставляем такой же, какой был у исходной переменной, непосредственно содержащей нужное нам значение. В рассматриваем примере это однобайтовая переменная.

10. Рассчитываем смещение. Запускаем вендовый калькулятор в шестнадцатиричном режиме и из адреса исходной переменной ( 07B2D52B ) вычитаем адрес, который искали в пункте 6 ( 07AF0020 ). Полученное число ( 3D50B ) вставляем во второе снизу поле над адресом. Впрочем, это же смещение и так было в окне информации дебаггера. Если знать, на что смотреть — можно не считать. Заодно можно и описание какое-нибудь вбить.

Картинка №3.
[​IMG]

11. Жмем “Ok”. В принявшей нужный нам вид строке в нижнем окне CE кликаем правой кнопкой по значению в колонке “Value” и выбираем пункт “Show as decimal”, чтобы число наших денег/здоровья/патронов показывалось в десятичном формате, для удобства. Удаляем из нижнего окна все лишнее и сохраняем результат в виде файла *.CT. Теперь не надо будет каждый раз после запуска игры заново искать нужные переменные, достаточно загрузить этот файл в CE.

PS: Спойлер. Чтобы не считать смещение, достаточно посмотреть на команду, в примере — eax+ebx. Внизу в окне информации дебаггера был список значений. Смотрим значения регистров, eax — это адрес, тот же самый, который мы копировали в поиск. Следовательно, ebx — смещение.

1. Начинаем точно также. Находим адрес нужной переменной и копируем его вниз.

2. Правой кнопкой кликаем по строке в нижнем окне и выбираем пункт “Pointer scan for this adress”. В появившемся окне ничего не меняем, жмем “Ok”.

[​IMG]

3. Появится окно с предложением сохранить файл, вводим какое-нибудь имя и подтверждаем. Начнется обработка, которая может занять довольно длительное время. По окончании выскочит окно с результатом.

[​IMG]

4. Закрываем все окна, кроме основного окна СЕ. Выходим из игры. Запускаем игру заново. В окне CE снова открываем процесс игры. Отвечаем “No” на вопрос, хотим ли мы сохранить ранее найденные результаты.

5. В основном окне CE слева, над нижним окном, нажимаем кнопочку “Memory View”. Откроется новое окно с дампом памяти, в верхнем меню которого следует выбрать Tools -> Pointer Scan. После чего снова появится уже знакомое нам окно указателей.

6. В окне указателей в верхнем меню выбираем File -> Open и открываем сохраненный в пунте 3 файл. Теперь в верхнем меню уже этого окна выбираем Pointer scanner -> Rescan memory — Removes pointers not pointing to the right adress. В появившемся новом окне ставим переключатель вверху в положение “Value to find” и вводим в поле ниже число, соответствующее тому, какое значение имеет в игре нужный нам параметр на данный момент. И указываем справа его тип, тот же самый, что мы искали в самый первый раз. (Как вариант — ищем не по значению, а по адресу, если не поленились найти его заново). Жмем “Ok”, снова появляется окно для сохранения файла, сохраняем. Можно перезаписать старый. Теперь процесс занимает намного меньше времени и некоторое количество результатов отсеивается.

[​IMG]

7. Повторяем пункты 4-6 раз пять или больше, пока количество результатов не перестанет уменьшаться заметно.

8. Когда количество найденных результатов стало более-менее стабильным, начинаем отлавливать среди них нужный. Сортируем значения по “Base Adress” и выбираем штук пять из тех, которые относятся к основному процессу игры. Если таковых не осталось — тогда сойдет и из игровых модулей. Выбираем наугад и дважды кликаем по каждому, после чего готовые указатели копируются в нижнюю таблицу основного окна CE.

9. Продолжаем выполнять пункты 4-6 с той разницей, что при перезагрузке процесса игры теперь сохраняем ранее найденные результаты. После каждой загрузки процесса смотрим нижнюю таблицу и выкидываем адреса, чье значение не соответствует актуальному игровому параметру или вообще осталось не определено. После этого грузим, как ранее, Pointer scanner, проводим новую фильтрацию и добавляем в нижнюю таблицу несколько свежих адресов.

10. Продолжаем развлекаться до достижения стабильного результата — пока хотя бы один из записанных в нижней таблице адресов не будет при каждом запуске игры неизменно показывать актуальный результат. Собственно, вот оно и есть, что нам и требовалось.

11. Выкидываем из таблицы все лишнее и сохраняем нужное в файл *.CT. Удаляем созданный при сканировании указателей файл(ы) *.PTR и огромную кучу накопившихся к тому времени временных файлов. Впрочем, с удалением можно и погодить пару дней, на случай, если после пары перезагрузок найденные адреса все-таки слетят. Т.е. окажется все-таки не то. Чтобы можно было продолжить с того момента, на котором остановились, а не начинать все заново.

Thread: Поиск базового адреса, смещения. Jade Dynasty

Veteran Join Date 2010 Mar Posts 2,222 Thanks 211 Thanked 2,225 Times in 290 Posts Rep Power 10

Поиск базового адреса, смещения. Jade Dynasty

В целом, процедура абсолютно одинакова для большинства программ, посему, раз прочитав подобное руководство для другой игры, которых хватает на нашем ресурсе, читать уже для Jade Dynasty особого смысла нет. Хотя, здесь я приведу несколько дополнительных вещей и пояснений.

Note:
— здесь не дается определения базовому адресу, смещениям и т.п.
— здесь нет детального описания функций и окон CE
— здесь нет детального описания ассемблерных команд
— данное руководство на 90% применимо и для Perfect World’а, т.к. движки у этих двух игр одинаковые.
— перед многими значениями стоит символ «$». Используется для идентификации шестнадцатеричных величин

Приступим.
Загружаем клиент Jade Dynasty, запускаем Cheat Engine (CE) и присоединяем последний к процессу игры «elementclient.exe». Объектом поиска будет самая доступная величина – количество жизней (HP), в моем случае, равная 779 единиц.
В поле «Value» CE вводим значение 779, выбираем тип сканирования «Exact value» и тип величины «4 bytes», и начинаем сканирование «first scan»

По завершению первого сканирования, CE отобразит список адресов (если он не очень велик), значения которых в момент поиска равнялись 779. Таких адресов может быть 10, может быть и сотни тысяч. Поэтому, нам требуется точно определить нужный адрес. Возвращаемся в игру и любым способом меняем значение HP, например, снимем с чара какую-либо вещь, дающую прирост HP:

Как только значение изменится, продолжаем поиск в CE, при этом тип сканирования оставляя «Exact value»

Найдено 4 адреса. Можно еще раз изменить значение HP и посмотреть, как будут меняться значения у этих адресов.

Через несколько секунд становится ясно: два первых адреса не имеют отношения к искомой величине, а два последних – текущее значение HP и максимальное.

Добавляем оба адреса в список и, выбрав «HP max», вызываем контекстное меню.

Нам требуется отыскать участок кода, который записывает значения в выбранный адрес $1265F098: «Find out what writes to this address»

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

Посмотрим дополнительную информацию для инструкции по адресу «$0046b5ca»:

CE отобразит окно, где будут указаны значения регистров и дизассемблированный участок кода клиента; инструкция mov [esi + $288], ecx как раз и отвечает за запись величины HP по адресу $1265F098, а значение указателя равно $1265EE10.
Смещение: +288.
$1265EE10 – следующая величина для поиска.

Note: если не будет найдено ни одного значения, значит требуется повторить процедуру по определению значения указателя (просмотр списка опкодов)

Берем первый из найденных адресов (совсем не обязательно, что данный адрес даст какой-либо результат), добавляем его в список (название +$288 для более легкой идентификации смещений) и теперь начнем поиск того, что обращается к данному адресу

Список опкодов достаточно велик, что, по большей части, свидетельствует о правильности поиска.

Рассмотрим две инструкции.
первая по адресу: $004c2ad2

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

вторая по адресу: $004d11c8

Человек, знакомый с ассемблером, сразу видит результат… К этому результату вернемся чуть позже, т.к. завершим процедуру поиска базового адреса по выработанному алгоритму.
$039F6E20 – следующая величина для поиска.

Итог:
[Базовый адрес] = $00B03064
[BA] + $1C + $28 + $288 – Максимальное значение HP

Please, post your questions on forum, not by PM or mail

I spend my time, so please pay a little bit of your time to keep world in equilibrium

So let’s go through the Cheat Engine Tutorial (x64).

So open Cheat Engine, then in the main menu select help then select Cheat Engine Tutorial.

File:Tutorials.CETutorialx64.01.png

Then attach to the Cheat Engine Tutorial process, it should be ‘Tutorial-x86_64.exe’.

If unsure how to attach to the process see: How to attach to a process

Step 1: Welcome

When the tutorial launches you should see some thing like this, you can just click the next button after reading the help text.

Save the password in later steps in case of crashes (from injections) and for restarting at a later time.

File:Tutorials.CETutorialx32.02.png

Step 2: Exact Value scanning

So for step 2 you will see some thing like this.

File:Tutorials.CETutorialx32.step01.01.png

What we need to find is the health, and here it’s an integer.

So setup the memory scanner to find an integer and for exact value scan then set the value to the current health value, most integers will be stored in a 4 byte variable, so let’s start there.

Note: Integers can be stored in a 1 byte variable (byte), 2 byte variable (int16/short), 4 byte variable (int32/int), or 8 byte variable (int64/long).

When ready click the first scan button.

File:Tutorials.CETutorialx64.step02.01.png

You Should see a list of addresses, in the found address list, like this.

File:Tutorials.CETutorialx64.step02.03.png

Now Click the hit me button, then reenter the current value and click the next scan button.

Note the red value in the list, this shows that the value has changed.

File:Tutorials.CETutorialx64.step02.04.png

After clicking next scan you may need to keep clicking hit me and rescanning tell the found address list is small enough to work with.

File:Tutorials.CETutorialx64.step02.05.png

Just double click the address in the found list to add it to the cheat table.
Then change the value and freeze the address, double click the value in the address list to edit it, freeze it by clicking the enabler/freeze box.

File:Tutorials.CETutorialx64.step02.06.png

Now the next button should be enabled, click it to go to the next step.
Click the hit me button again if the next button is not enabled already.

Step 3: Unknown initial value

When you start step 3 you should see the form looking like this.

File:Tutorials.CETutorialx32.step03.01.png

Like the help text said make sure to click the new scan button before starting new scans.

File:Tutorials.CETutorialx64.step03.02.png

This clears the found results to start scanning for a new value.

Here is where I suggest going ahead and clicking the hit me button, just to see how the value is decreased to help in determining what value type to scan for.

File:Tutorials.CETutorialx32.step03.03.png

Note that the value was decreased by an integer, that is a non fractional number.

So I would setup the scanner for 4 bytes and unknown initial value.
Then click the first scan button.

File:Tutorials.CETutorialx64.step03.04.png

Now click the hit me button.

Then set the scan type to decreased value and click the nest scan button.

File:Tutorials.CETutorialx64.step03.05.png

Note the number of found addresses, this is kinda small for most games these days, the found results can easily be in the millions for most games.

Now just keep decreasing the value with the hit me button, and scanning for a decreased value, until the found results is small enough to work with.

File:Tutorials.CETutorialx64.step03.06.png

Now we just pick an address and change the value to see if it has the desired effect, this is just how it works.

Here is where I suggest that you always note the values (or just Ctrl+C) before changing them to set them back if they are not the right value, to keep from changing a bunch of unknown addresses and corrupting your save files when doing this in games.

The next button should become enabled as soon as you set the value to 5000.
After changing the value and clicking the hit me button the progress bar should fill, but this is not needed.

File:Tutorials.CETutorialx64.step03.07.png

Now the next button should be enabled, click it to go to the next step.
Click the hit me button again if the next button is not enabled already.

Step 4: Floating points

When you start step 4 you should see the form looking like this.

File:Tutorials.CETutorialx32.step04.01.png

So click the new scan button.
Then setup the scanner for a float, exact value, enter the current health value.

When setup click the first scan button.

File:Tutorials.CETutorialx64.step04.02.png

So just scan like before to find the health address, then add it to the address list.

Now click the new scan button again.
Then setup the scanner for a double, exact value, enter the current ammo value.

When setup click the first scan button.

File:Tutorials.CETutorialx64.step04.03.png

So just scan like before to find the ammo address, then add it to the address list.

Now change the values to 5000, then the next button should become enabled.
Then click the next button to progress to the next step.

Step 5: Code finder

When you start step 5 you should see the form looking like this.

File:Tutorials.CETutorialx32.step05.01.png

So first find the value then add it to the address list.

Go ahead and save the table and the password at this point, just in case the debugger isn’t setup right.

If you need help setting up the debugger see: Debugger options

After you have the address in the address list right click it then select find out what accesses this address.

File:Tutorials.CETutorialx32.step05.02.png

Cheat Engine will prompt you about attaching the debugger, just click the yes button.

File:Tutorials.CETutorialx32.step05.03.png

Then a debugger form will open, now click the change value button, and you should get code that shows up in the debugger form.

What we want is a write instruction. So we will be looking for some thing like one of the following:

mov [**],**
add [**],**
sub [**],**
*** [**],**

Select the code line of the write instruction, you can click the show disassembler button to see the code in memory, then click the replace button.

Don’t forget to click the stop button.

File:Tutorials.CETutorialx64.step05.04.png

The replace button will replace that line of code with NOPs.

Cheat Engine will prompt you for a name for the entry it will add in the advanced options list.

Enter a name and click the OK button.

File:Tutorials.CETutorialx64.step05.05.png

Now click the change value button back on the tutorial.

The next button should become enabled, then click the next button to advance to the next step.

When entries in the advanced options list are replaced, they will show up with red text.

File:Tutorials.CETutorialx64.step05.06.png

The advanced options list can be viewed by clicking the advanced options button in the status bar on the bottom left corner of the Cheat Engine main form.

To restore the original code for an entry in the list, right click the entry and select restore with original code.

File:Tutorials.CETutorialx64.step05.07.png

Note that the text is black after restoring.

File:Tutorials.CETutorialx64.step05.08.png

Step 6: Pointers

When you start step 6 you should see the form looking like this.

File:Tutorials.CETutorialx32.step06.01.png

So first find the value then add it to the address list.

After you have the address in the address list right click it then select find out what accesses this address.

File:Tutorials.CETutorialx64.step06.05.png

Then click the change value button, to have the process access the address.

When choosing the code to find the base address for the pointer, try to select an instruction that doesn’t write to the same register as the base address.

Here we’re interested in the value between the square brackets (‘[‘ and ‘]’), so here we want the value of RDX.

File:Tutorials.CETutorialx64.step06.02.png

The offset here is 0, if the instruction had some thing like this:

mov [rdx+12C],eax

Then the offset would be ’12C’ (0x12C), note that this is in hex.

Now set the scanner for 8 bytes, exact value, check the hex check box, then take the value found and put that as the value to scan for.

When ready click the first scan button.

Look in the found address list for address with green text, these are static addresses.

File:Tutorials.CETutorialx64.step06.03.png

Add one to the cheat table, double click the address of the memory record that was added to the address list, copy the address then check the pointer check box, and paste the address in the pointers base address.

If you are unsure how to do this look here: How to add addresses to the address list

So my pointer will look like this.

["Tutorial-x86_64.exe"+XXXXXX]+0

It should be setup some thing like this, remember to set the offset to the offset you found.

File:Tutorials.CETutorialx64.step06.04.png

Click the OK button when the pointer is setup.

Now freeze the value at 5000 and click the change pointer button, the next button should become enabled.

If the next button doesn’t become enabled then select another address from the found list, look for a green one that had it’s value changed, and set it up like the last one and see if it points to the right value, if so change the value freeze and click the change pointer button.

Click the next button to advance to the next step.

Step 7: Code Injection

When you start step 7 you should see the form looking like this.

File:Tutorials.CETutorialx32.step07.01.png

Here we’ll follow the the same procedures as step 5, but instead of clicking replace click the show disassembler button.

File:Tutorials.CETutorialx64.step07.02.png

This will open the disassembler view form at the instruction’s address.

File:Tutorials.CETutorialx64.step07.03.png

With the instruction selected press Crtl+A, to open an auto assembler form.

In the auto assembler form menu select template then select full injection.

File:Tutorials.CETutorialx32.step07.04.png

This will generate some script to start you out.

File:Tutorials.CETutorialx64.step07.05.png

Now we need to add some code that will increase the value by 2, then remove the original code that decreases the value.

For increasing the value we can use INC or ADD.

So let’s try some thing like this.


newmem:
add dword ptr [rsi+780],2

code:

//sub dword ptr [rsi+00000780],01
jmp return

address:

jmp newmem
nop
nop
return:

Now add the script to the cheat table.

If you are unsure how to do that look here: How to add script to table

Then enable the script and click the hit me button.

This should enable the next button, so click the next button to go to the next step.

Step 8: Multilevel pointers

When you start step 8 you should see the form looking like this.

File:Tutorials.CETutorialx32.step08.01.png

Manual Iteration

So here we will follow the same steps as step 6, except we’ll see what accesses the base address we find, and we’ll keep repeating this until a static base is found.

So here is my first debugger output.

10002D8D1 - B9 A00F0000 - mov ecx,00000FA0
10002D8D6 - E8 3522FEFF - call Tutorial-x86_64.exe+XXXXXX
10002D8DB - 89 46 18  - mov [rsi+18],eax  <<<<<<
10002D8DE - 89 C2  - mov edx,eax
10002D8E0 - 48 8D 4D F8  - lea rcx,[rbp-08]

RAX=00000000000007F7
RBX=000000000125CD60
RCX=0000000000000FA0
RDX=00000000828087F3
RSI=0000000001287960  <<<<<<
RDI=0000000100258308
RSP=000000000102F070
RBP=000000000102F0B0
RIP=000000010002D8DE
R8=0000000100161BA0
R9=00000000008E06A0
R10=0000000000000002
R11=0000000000000206
R12=00000000012607C0
R13=0000000100161BA0
R14=0000000100258300
R15=0000000100257A18

I did find a static base on the first scan of the base address but I remember this being a false base.
So here what we want is a base address in the form of ‘process.exe+offset’, you can try one of the others that look like ‘module.dll+offset’ but I want to say that here they will prove to be false pointers. And yes most newer games will have many false values and pointers.

And the debugger output from the address holding: 0000000001287960

10002D88B - E8 90961200 - call Tutorial-x86_64.exe+XXXXXX
10002D890 - E9 65000000 - jmp Tutorial-x86_64.exe+XXXXXX
10002D895 - 48 83 3E 00 - cmp qword ptr [rsi],00  <<<<<<
10002D899 - 74 5F - je Tutorial-x86_64.exe+XXXXXX
10002D89B - 48 8B 36  - mov rsi,[rsi]

RAX=0000000000013117
RBX=000000000125CD60
RCX=000000000125CD60
RDX=0000000000003CE3
RSI=0000000002D6D540  <<<<<<
RDI=0000000100258308
RSP=000000000102F070
RBP=000000000102F0B0
RIP=000000010002D899
R8=0000000100161BA0
R9=00000000008E06A0
R10=0000000000000002
R11=0000000000000206
R12=00000000012607C0
R13=0000000100161BA0
R14=0000000100258300
R15=0000000100257A18

And the debugger output from the address holding: 0000000002D6D540

10002D845 - E8 D6961200 - call Tutorial-x86_64.exe+XXXXXX
10002D84A - E9 AB000000 - jmp Tutorial-x86_64.exe+XXXXXX
10002D84F - 48 83 7E 18 00 - cmp qword ptr [rsi+18],00  <<<<<<
10002D854 - 0F84 A0000000 - je Tutorial-x86_64.exe+XXXXXX
10002D85A - 48 8B 76 18  - mov rsi,[rsi+18]

RAX=00000000000166D2
RBX=000000000125CD60
RCX=000000000125CD60
RDX=000000000000302E
RSI=0000000002D6CE40  <<<<<<
RDI=0000000100258308
RSP=000000000102F070
RBP=000000000102F0B0
RIP=000000010002D854
R8=0000000100161BA0
R9=00000000008E06A0
R10=0000000000000002
R11=0000000000000206
R12=00000000012607C0
R13=0000000100161BA0
R14=0000000100258300
R15=0000000100257A18

And the debugger output from the address holding: 0000000002D6CE40

10002D800 - E8 1B971200 - call Tutorial-x86_64.exe+XXXXXX
10002D805 - E9 F0000000 - jmp Tutorial-x86_64.exe+XXXXXX
10002D80A - 48 83 7E 10 00 - cmp qword ptr [rsi+10],00  <<<<<<
10002D80F - 0F84 E5000000 - je Tutorial-x86_64.exe+XXXXXX
10002D815 - 48 8B 76 10  - mov rsi,[rsi+10]

RAX=000000000000B567
RBX=000000000125CD60
RCX=000000000125CD60
RDX=00000000000050A1
RSI=000000000123F1C0  <<<<<<
RDI=0000000100258308
RSP=000000000102F070
RBP=000000000102F0B0
RIP=000000010002D80F
R8=0000000100161BA0
R9=00000000008E06A0
R10=0000000000000002
R11=0000000000000206
R12=00000000012607C0
R13=0000000100161BA0
R14=0000000100258300
R15=0000000100257A18

Now we scan for that base ‘000000000123F1C0’ and you should find a static address, but in real games you would keep going until a static base is found.

With that static address as the base my pointer will look like this.

[[[["Tutorial-x86_64.exe"+XXXXXX]+10]+18]+0]+18

File:Tutorials.CETutorialx64.step08.02.png

Pointer Scan

The pointer scan can be used to solve this quickly by first finding the address of the desired value, saving a generated pointer map, restarting the game, searching for the address again, saving another pointer map, and then comparing the two. More information can be found in Help_File:Pointer_scan.

Finally

After you have found the pointer, freeze it at 5000, then click the change pointer button.
If you found the right base the next button should become enabled after about 2 seconds.
So click the next button to go to the next step.

Step 9: Shared code

When you start step 9 you should see the form looking like this.

File:Tutorials.CETutorialx32.step09.01.png

So here like the help text says there is far more than one solution.

First we need to find one of the addresses and add it to the table.

If you are having trouble finding an address, remember to try different value types, and don’t forget to start new scans.

Then like in step 7 we want to see what accesses the address, to find the function that writes to the actor’s health.

Go ahead and save the password if you want to try different ways, this is the last step in the tutorial.

So here it’s good to understand what we’re actually looking for to tell allies and combatants apart.

When the game or engine is written, actors and players might be written like this.

//// Actor, base for all actors
class Actor(object){
string Name = ‘Actor’;
Coord Coords = new Coord(0, 0, 0);
float Health = 100.0;

}
//// Player
class Player(Actor){ //// Player inherits form Actor
string Name = ‘Player’;
int Team = 1;

}

The team it self could be a structure, say if it’s declared as an object class like the ‘Coords’ variable, which we would want to look for a pointer to the actor’s team structure.

So one way we could do this is to find the team id or team structure in the player structure.

Find the team id in the player structure

After you have found the function that decreases health.

Right click the instruction in the disassembler view form, and select find out what addresses this instruction accesses.

File:Tutorials.CETutorialx64.step09.02.png

Then click the attack button for all 4 values.

You should have all 4 addresses in the debugger list.

File:Tutorials.CETutorialx64.step09.03.png

So go ahead and add them to the address list.

File:Tutorials.CETutorialx64.step09.04.png

Then let’s open the dissect data structure form.

File:Tutorials.CETutorialx64.step09.05.png

You’ll get some pop ups, after going thought them you should see a form like this.
Note that I had to expand the width of the form to be able to move the columns.

Now on mine offset 0x10 was guessed as a pointer which is 8 bytes wide in a 64 bit process. I saw that the pointers at 0x10 had values that really didn’t look like pointers.

So I had to switch it to 4 byte, and add a new element set it’s offset to 0x14 with 4 byte value type. This is often the way it works.

File:Tutorials.CETutorialx64.step09.06.png

So here we can see that the team variable is at offset 0x14 of the structure.

Now we need to add some injection code to a script, then add some code that checks the team variable of the structure, to determine which actors are allies and which are combatants.

So we want some this like this.

File:Tutorials.CETutorialx64.step09.07.png

So with this script enabled,
when the game writes to an actors health here is what will happen after the jump to the hook code:

  1. Save (PUSH) the RFLAGS register, not completely needed but still a good habit when comparing.
  2. Check if actor is on team 1.
    1. If actor is on team 1, then we set the new value to 5000 in a floating point format.
  3. Check if actor is on team 2.
    1. If actor is on team 2, then we set the new value to 0 in hex format. (float 0 == int 0 == hex 0)
  4. Restore (POP) the RFLAGS register, this is completely needed if the register was PUSHed.

With this script enabled, click the restart game and autoplay button, then you should see the form change and look like this.

File:Tutorials.CETutorialx32.step09.08.png

So click the next button to complete the tutorial.

Then you should see a form telling you that you have completed the tutorial.

Find a difference in the registers

After you have found the function that decreases health.

Right click the instruction in the disassembler view form, and select find out what addresses this instruction accesses.

File:Tutorials.CETutorialx64.step09.02.png

Then click the attack button for all 4 values.

You should have all 4 addresses in the debugger list.

File:Tutorials.CETutorialx64.step09.03.png

Now let’s look at the registers to see if we can find a difference in the allies and combatants.

Select each address individually and press Ctrl+R.

Arrange the forms to make it easier to compare.

File:Tutorials.CETutorialx64.step09.b.01.png

So here we can see that RSI is 1 for the combatants.

So a script like this should work.

File:Tutorials.CETutorialx64.step09.b.02.png

So with this script enabled,
when the game writes to an actors health here is what will happen after the jump to the hook code:

  1. Save (PUSH) the RFLAGS register, not completely needed but still a good habit when comparing.
  2. Check if RSI register is 1.
    1. If RSI register is 1, then we set the new value to 0 in hex format. (float 0 == int 0 == hex 0)
    2. If RSI register is not 1, then we assume the actor is an ally so we set the new value to 5000 in a floating point format.
  3. Restore (POP) the RFLAGS register, this is completely needed if the register was PUSHed.

With this script enabled, click the restart game and autoplay button, then you should see the form change and look like this.

File:Tutorials.CETutorialx32.step09.08.png

So click the next button to complete the tutorial.

Then you should see a form telling you that you have completed the tutorial.

See also

  • Tutorials
  • Syntax Highlighter
Эти загадочные указатели
Теоретическая часть для понимания сути процесса

Как доподлинно известно, адрес в оперативной памяти — некий условный шестнадцатеричный идентификатор (а еще проще — обычное число) для взаимодействия программы с реальной физической памятью. По конкретному адресу всегда расположен ровно 1 байт информации. При этом значение любой переменной в программе хранится по какому-то адресу, даже у переменной, для которой выделено больше 1 байта. Например, если по адресу 0CC9B840 хранится переменная в 2 байта со значением в 10000, то в памяти по адресу 0CC9B840 будет храниться 10h, а по адресу 0CC9B841 будет храниться 27h (10000 в hex-кодировке будет выглядеть как 1027 с учетом обратного порядка чередования байтов). Но для программы важен именно начальный адрес всего блока из 2 байтов. Просто программа знает, что нужно использовать последовательно 2 байта информации, начиная с байта по искомому адресу — соответствующий тип переменной использовался разъеботчиком при написании исходного кода.

Каждый кулхацкер сталкивается с тем, что ранее найденный адрес со значением здоровья, патронов или выпусков журнала «Горячие попки ванамингосов» после перезапуска игры, уровня, а то и вовсе ВНЕЗАПНО становится нерабочим — теперь в нем хранится какое-то другое значение или вообще мусор (так называемый garbage — данные, на которые больше нет никаких ссылок). И нужно, превозмогая боль и страх, со штангенциркулем искать новый вожделенный адрес. Происходит сие непотребство из-за так называемого динамического распределения памяти — механизма создания и хранения программами данных в оперативной памяти непосредственно в процессе работы самой программы (называется «в рантайме»). Нужно игре выделить какой-то диапазон адресного пространства под свои данные? Да пожалуйста. Во время своей работы игра жонглирует данными постоянно, выделяя и освобождая память по необходимости. Создался новый враг? Отлично, под его данные выделено N байт. Враг получил передозировку киберсвинца в организьме и больше он нам и нахуй не нужон, враг ваш? Память, ранее выделенную под него, можно освободить и сделать доступной для записи каких-то иных данных, например, для спавна другого врага.

И, собственно, из-за этого блядского цирка с пересозданием объектов и происходит переезд данных на новые адреса в памяти, например, при переходе на новый уровень — игре нужно заново создать уровень и наполнить его объектами. При этом игре лишь нужно знать, с какого адреса будут начинаться данные нового уровня (увязать все это взаимодействие — задача компилятора, программист и в страшном сне заниматься этим не захочет), а относительно этого адреса игра будет пользоваться так называемыми указателями (pointers) на адреса — ссылками на начальные адреса вновь создаваемых объектов согласно принятой иерархии или на адреса, по которым располагаются конкретные значения.
Исходя из сказанного, можно сделать следующие выводы:
1. Память однородна. Ячейка памяти может хранить как исполняемый код, так и какие-то данные.
2. Если данные хранятся непосредственно по какому-то адресу в памяти, доступ к ним осуществляется напрямую при обращении к этому адресу.
3. По какому-то адресу в памяти можно разместить не конечные данные, а указатель — ссылку на какой-то другой адрес, где хранятся нужные данные.
4. Если указатель ссылается на другой указатель, то такой указатель будет называться многоуровневым. Времена одноуровневых указателей давно в прошлом, поэтому 99% нужных нам указателей будут многоуровневыми.
06

Тут надо сделать маленькое лирическое отступление о причинах возникновения такого порядка вещей. Всему виной повсеместное использование ООП — объектно-ориентированного программирования. Такой подход позволяет оперировать объектами игры как с некими абстрактными структурами данных, и хранить однотипные данные не в обособленных переменных, а в заранее определенном формате. Например, при создании нового объекта типа пони можно последовательно хранить имя пони, тип пони (обычная поня, пегас, единорог, аликорн и т.д.), цвет пони в кодировке RGB, тип кьютимарка (отличительный знак на ляжке — пояснение для жалких унтерменшей, не смотревших прогрессивные мультики про поней). Таким образом, когда будет создан новый объект типа пони, информация о нем будет заранее определенным образом упорядочена при описании структуры или класса объекта.

На абстрактном языке погромирования это может выглядеть следующим образом:

struct Pony
{
char name[10]; // 10 байт под имя
short type; // 2 байта под тип пони
byte color[3]; // 3 байта под цвет
byte cutiemark; // 1 байт под тип кьютимарка
}

02

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

Теперь представим, что мы как-то адекватно хотим описать в коде игры возможность пегасов летать. Создадим некую структуру данных и назовем ее PonyType. Пусть другие типы, кроме пегасов, нас сейчас не волнуют, и мы просто хотим в один байт уместить информацию о возможности или невозможности пони летать.
Для этого объявим в этой структуре логическую переменную canFly типа bool, которая может принимать значения true либо false, занимая при этом ровно 1 байт. Также добавим еще 1 байт под запас энергии для полета.

struct PonyType
{
bool canFly;
byte energy;
}

А теперь начинается интеграционная дружбомагия. В исходную структуру типа Pony поместим указатель на структуру типа PonyType:

struct Pony
{
char name[10];
PonyType* type; // тот самый указатель на тип в виде звездочки *
byte color[3];
byte cutiemark;
}

Таким образом, при создании нового объекта типа Pony информация о возможности летать и запасе энергии будет передаваться через указатель на структуру PonyType. Чсх, в памяти это будет занимать те же самые 2 байта, как если бы использовали ранее применявшийся целочисленный тип short.
02
Для программиста, который занимается разработкой структуры типа Pony, теперь не имеет значения, что там внедряет другой программист, занимающийся разработкой структуры PonyType. При правильной организации кода игры любые изменения в структуре PonyType никак не отразятся на работоспособности структуры Pony. Собственно, ради этого все и затевалось. Конечно, на таком простом примере сложно ощутить все преимущества ООП, но мейнстримом он стал не за красивые глаза.
И теперь уже совсем нетрудно построить некоторую цепочку указателей, начиная от создания мира.
Остальные данные структур не отображены для экономии пространства:

// структура лучезарного эквестрийского мира с множеством локаций
struct Equestria
{
.. // какие-то другие данные
City* city[36]; // для выделения памяти под 36 объектов типа City
}

// структура уютненького Понивилля с множеством поняшек
struct City
{
..
Pony* pony[100]; // для выделения памяти под 100 объектов типа Pony
}

// структура няшечки Рейнбоу и любой другой пони
struct Pony
{
..
PonyType* type; // для выделения памяти под 1 объект типа PonyType
}

// структура типа пони
struct PonyType
{
..
byte energy; // для выделения 1 байта памяти под значение энергии
}

03

Таким образом, например, при перезапуске игры нужно вновь создать объект типа Equestria, записав его начало по новому адресу, а дальше последовательно будет выделена память под 36 объектов типа City, для каждого и которых будет последовательно выделено место под 100 объектов типа Pony, в каждом из которых, в свою очередь, будет выделено место под 1 объект типа PonyType.
Адресация во всех этой махине зависит полностью от одного адреса — адреса начала объекта типа Equestria. И если нас интересует конечный адрес количества энергии на полет конкретной пони, то он каждый раз будет новый. Но с точки зрения игры все остается по прежнему:
Она знает начальный адрес, по которому прописался исполняемый код игры.
От него она отсчитывает известное количество байт, занимаемое исполняемым кодом и не относящимися к генерации уровня данными, и там начинает записывать данные, уже относящиеся к генерации уровня — то есть создает объект типа Equestria.
Далее по системе указателей она доходит до конечного пункта — количества энергии у конкретной пони — и использует его.

На слове последовательно я акцентирую внимание по той причине, что по нашей схеме будет использовано сквозное заполнение адресного пространства оперативной памяти: сначала будут созданы какие-то исходные данные объекта типа Equestria, затем создан первый объект типа City и все объекты типа Pony, затем второй объект типа City и все объекты типа Pony и так далее.

Собственно, зная адрес начала объекта типа Equestria и последовательность смещений до энергии конкретной пони, мы всегда может восстановить искомый адрес энергии. Задача поиска рабочего адреса энергии возлагается на рабочий указатель.
В рассматриваемом случае буквально получается, что объект типа Equestria создается по адресу, по которому была записана в оперативную память сама игра (ее исполняемый файл Game.exe) плюс некоторое смещение 10EF0C, необходимое игре для записи своего исполняемого кода и каких-то иных данных.
Относительно адреса объекта типа Equestria на значение C0 смещено расположение конкретного объекта типа City, относительно которого на значение FAD8 смещено расположение конкретного объекта типа Pony, относительно которого на значение 02FF смещено расположение конкретного объекта типа PonyType, относительно которого на значение 01 смещено расположение искомого значения энергии.
Обладая этой информацией, мы почти всегда однозначно можем определить истинный адрес искомого значения энергии.
04
05

И все это благолепие справедливо, конечно же, в том случае, если нужный нам город и нужная нам поня создаются первыми (вторыми, десятыми, главное — стабильность положения относительно начала) во всем массиве объектов (что часто бывает, поскольку именно относящиеся к игроку данные загружаются первыми). Также нужно уточнить, что адрес объекта типа Equestria может и не быть статическим (то есть неизменным от загрузки к загрузке — в нашем случае это Game.exe + 10EF0C), если не предопределен конечный объем данных перед его загрузкой. В этом случае адрес начала ключевого объекта должен быть найден способами, не входящими в данный гайд (через поиск так называемых сигнатур — уникальных последовательностей байтов). О сигнатурах мы тоже когда-то обязательно поговорим.

Резюмируя затянувшуюся вводную часть, подчеркну, что никакой мистики тут нет. С точки зрения кулхацкерства указатель — всего лишь ссылка на начальный адрес объекта внутри адресного пространства другого объекта либо непосредственно на адрес, по которому расположены требуемые данные.
Для успешного кулхацкерства достаточно знать, как вся система примерно работает в целом, а также владеть автоматизированными средствами поиска и ручными методами контроля правильности указателей.
К этому и приступим.

Автоматический поиск указателей
Практическая часть для формирования навыков

Если речь идет не об эрпогэ, где достаточно один раз взломать значение каких-нибудь скиллпоинтов, а затем тупо вкачать героя на полный фарш, поиск указателей является обязательной процедурой. Играя в шутер или РТС, хочется всегда иметь под рукой возможность поковырять ресурсы, здоровье или боезапас, не утруждая себя подчас утомительной процедурой поиска нового адреса. Да и в пошаговой эрпогэ каждый бой искать количество очков действия, если их всего два, а то и одно — нахуй так жыдь?
Но на помощь кулхацкерам приходят средства автоматизации СЕ, благодаря которым поиск рабочих указателей является ничуть не более сложной процедурой, чем стандартный поиск адреса.

Процесс поиска предлагаю рассмотреть на примере первой Мафии. Если при взгляде на главное меню у тебя заиграла в голове та самая музыка, а сердце защемило тоской по былому — я тебя понимаю!
mafia_main

Ну что ж, искать мы будем указатель на адрес, по которому хранится количество патронов в магазине оружия, находящегося в руках у игрока. Это весьма полезный указатель, позволяющий нам забыть про необходимость перезарядки абсолютно любого вооружения. Здесь и далее я считаю, что поиск соответствующего адреса не вызывает проблем. Если таковые имеются, можно либо перечитать вступительный гайд по СЕ, либо задать вопрос тут в каментах.
Значение патронов в текущем оружии хранится в обычной целочисленной переменной 4 байта, никаких трудностей при поиске и отсеве возникнуть не должно даже у новичков в использовании СЕ.
Game 2021-03-01 23-16-51-049

Итак, значение найдено. Действующий адрес 16В4744С.
m01_0

Жмакаем правой кнопкой по адресу и выбираем пункт Pointer scan for this address.
m02_0

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

Далее СЕ попросит указать расположение файла результатов сканирования указателей в формате PTR.
m04

После появится окно прогресса сканирования.
m05

И завершится все феерическим количеством найденных указателей (результаты уже сохранены в файл 111.PTR). Предлагаю внимательно изучить результаты.
Сверху слева в выпадающем списке указан формат представления данных по конечному адресу, на который ссылается указатель. В нашем случае это 4 байта целочисленное. Конечный адрес указан в крайнем правом столбце Points to.
Сверху по центру указано общее количество найденных указателей: 26769689. 26 мульёнов — шутка ли?
В крайнем левом столбце Base Address указывается базовый статический адрес, относительно которого выстраивается многоуровневый указатель.
В столбцах Offset 0Offset 6 указывается смещение на каждом уровне многоуровневого указателя.
m06

Если перенести даблкликом найденный указатель в рабочее поле СЕ и даблкликом же в поле Address открыть окно редактирования Change address, можно будет детально изучить всю структуру добавленного указателя. В рабочем поле указатель помечается префиксом P->.
В окне Change address снизу по центру указаны по порядку все уровни указателя, начиная со статического базового адреса. Справа приводится результат по каждому смещению. Результат по предыдущему вычислению становится исходным адресом для следующего уровня.
m07_0
m08_0

Если говорить совсем в открытую, уже сейчас, несмотря на дикое количество результатов, с ними можно работать. Но мы из вредности проведем еще один цикл поиска и отсева. Главное правило при отсеве указателей заключается в максимальном смещении всего адресного пространства, выделяемого под игру. Чаще всего, правда, достаточно игру просто перезапустить, но можно и поизъёбствываться, запустив перед загрузкой игры пару «тяжелых» приложений.
Но мы ограничимся перезапуском самой игры и загрузкой другого уровня. Окно СЕ с результатами поиска указателей по старому адресу остается открытым.
Game 2021-03-01 23-26-48-480

В открытом окне СЕ снова выбираем процесс игры. Проводим поиск нужного адреса. Готово.
В окне поиска указателей можно обратить внимание на то, что ранее найденные указатели, отображенные в данный момент на экране, уже не ссылаются на нужный адрес в памяти игры.
m09_0

Теперь достаточно скопировать новый найденный адрес (даблклик по полю Address откроет окно редактирования), затем в окне поиска указателей нужно выбрать пункт меню Pointer scanner > Rescan memory – Removes pointers not pointing to the right address.
m10_0

В появившемся окне нужно проконтролировать корректность введенного адреса и отметить чекбоксик Only filter out invalid pointers.
После установки чекбоксика изменение адреса и типа отсева станет недоступно.
m11

Снова СЕ предложит нам сохранить результаты отсева указателей в файле PTR. Я рекомендую каждый этап сохранять в отдельной файле, ибо в случае каких-то проблем можно будет без труда загрузить предыдущие результаты. Из окна результатов сканирования Pointer scan это можно сделать через пункт меню File > Open.
m12

Результатом предыдущего этапа являются 3кк указателей, что почти на порядок меньше, чем при первом поиске.
m13_0

С этими результатами уже точно можно работать, но для подстраховки мы проведеи еще и третий цикл.
Game 2021-03-01 23-35-28-575
m15_0

И четвертый цикл отсева произведем не по результатам поиска нового адреса, а по результатам сравнения в текущей сессии значений по адресам, на которые ссылаются найденные указатели. В этом случае нужно изменить тип отсева на Value to find, указать тип значения 4 байта целочисленное и снять чекбоксик Only filter out invalid pointers.
m16
m17_0

Как видно из количества отсеянных указателей на третьем и четвертом этапах, эти этапы можно было смело пропустить.
И вот здесь кулхацкер задаст себе резонный вопрос: как же работать с тремя миллионами (Карл!) найденных указателей? На помощь пытливому уму придет логика и здравый смысл:
1. Нужно проанализировать базовые адреса в колонке Base Address. Всякие мутные ddl-ки и прочие модули нам вряд ли подойдут. Нужен исполняемый файл или, на крайний случай, какая-то базовая библиотека. В случае с Мафией ориентироваться на Game.exe более чем разумно.
2. Если после очередного отсева есть указатели, ссылающиеся на адрес с некорректным значением (при условии правильно указанного типа значения — целочисленного или с плавающей точкой!), то такие указатели нам явно не подходят.
3. Чем меньше уровней в многоуровневом указателе, тем надежнее будет его использование. Иногда, конечно, и двухуровневые указатели могут отваливаться, но желательно стремиться к меньшему числу уровней.
И именно на этом этапе анализа достаточно кликнуть по заголовку столбца Offset 6, чтобы указатели были рассортированы по количеству уровней.
m18_0

Теперь нужно легко и непринужденно даблкликом накидать скопом в рабочее поле СЕ указатели второго-пятого уровня различными базовыми адресами и первыми смещениями. В нашем примере очевидно, что Game.exe + 00246D4C является, скорее всего, тем самым правильным базовым адресом, но вот иерархия уровней указателей может не обязательно говорить в пользу меньшего количества уровней. Строго говоря, для точного ответа необходим ручной анализ структур данных, но это уже более комплексный вопрос. Поэтому надо накидать в рабочее поле штук по пять указателей каждого уровня и получить в итоге целую батарею предварительных результатов. В рабочем поле они все выглядят одинаково, но при редактировании можно будет увидеть всю структуру уровней каждого указателя.
m19m20
m21m22

Торжественный момент! Настало время проверить работоспособность найденных указателей.
Как всегда перезапускаем игру и загружаем другой уровень.
Game 2021-03-01 23-44-04-589

Выбираем в открытом окне СЕ процесс игры и — вуаля. Все найденные указатели являются рабочими. Удалять их из экономии пространства я пока не советую, ибо в разных условиях какие-то могут отвалиться и перестать работать, но в целом всё получилось. Можно объединить все указатели в группу в рабочем поле СЕ, выделив их и выбрав в контекстном меню по правой кнопке мыши Add to new group, озаглавив группу и выбрав уверенное No в вопросе «Do you want “address” version?». В настройках группы Group config в контекстном меню можно выбрать Hide children when deactivated и сворачивать всю группу пробелом или кликом по чекбоксу заморозки значения в левой части строки. Табличку можно сохранять и жить припеваючи.
m23

Анализ дампа в окрестностях найденного адреса показал, что за 4 байта до нашего адреса хранится тип оружия, а через 4 байта после — общее количество патронов к данному оружию.
m24

Что же нам теперь, повторять эту возню с поиском указателей еще и на эти адреса? Разумеется, нет.
Достаточно скопировать рабочем поле какой-нибудь корректный указатель с помощью Ctrl + C или пункта контекстного меню Copy, после чего вставить его здесь же по Ctrl + V (Paste).
Откроется небольшое окно, позволяющее указать требуемое смещение.
m25

Альтернативный вариант заключается в копировании всего указателя без изменений и последующего редактирования копии в общем порядке.
m26

Вот и все. Получается уютненькая табличка, которой можно пользоваться в том числе и тогда, когда оружия на миссию вообще не выдают, а покувыркаться с Сарочкой очень хочется — достаточно выставить тип оружия на 10 (томми-ган) и досыпать патронов, не забывая выкинуть его и поднять для корректной обработки появления оружия в руках у персонажа.
m27
sarah

P.S. Ежели гранд-мастера погромирования меня поправят в чем-то и подскажут более корректные формулировки, буду крайне благодарен. В рассматриваемых теоретических вопросах я дилетант, поэтому здоровая критика моего взгляда на проблему будет очень пользительной.

Добавить комментарий