Поиск и вызов игровых функций
-
[GTA Vice City] Простой спавн машин
Собственно, выйти на функции создания автомобилей очень просто - в игре есть читы, которые позволяют "вызывать" себе танк, катафалк и другие автомобили.
Поэтому выйдя на функцию обработки чита - мы найдем функции для создания автомобиля. Приступим.Для начала вспомним несколько читов для создания автомобилей.
Я выбрал чит-код создания катафалка -THELASTRIDE
, но так же можно использовать наш любимый чит создания танка -PANZER
.Собственно ввод кодов - это операция по сохранению текущего введенного символа в строку и последующая проверка получившейся строки на соответствие.
Каждый введенный символ записывается как в стек, поэтому в реальности эти читы выглядят как
REZNAP
иEDIRTSALEHT
.Но если поискать данный текст, мы его не найдем. Как же так спросите вы? На самом деле функция обработки чит-кодов срабатывает на вводе последней буквы, поэтому проще всего просто искать часть введенного кода.
Выйдем в меню игры, введем чит-кодTHELASTRIDE
и попробуем поискать подстрокуTSALEHT
(не забываем переворачивать чит-код) в игре:Ура, мы нашли текст по адресу
00A0F94F
. Не будем его запоминать, он нам не пригодится, а сразу посмотрим, что же творится в памяти.
Нажмем на адрес правой кнопкой мыши и выбираемBrowse this memory Region
.В памяти сразу поднимемся на пару строк вверх, обычно строки длиннее чем мы искали:
Ура, видим часть введенного нами кода. Так же могут присутствовать и другие куски текста, например на скришоте я немного побегал перед вводом чит-кода.
Теперь мы видим, что в реальности строка ввода чит-кода больше чем мы ожидали, а её начало находится там, где присутсвуют первые символыDW DIRTSAL
, т.е. по адресу00A0F94A
. Добавим этот адрес в табличку (размер строки вычислен эмпирическим путем - посчитал не нулевые байты):Теперь если в игре мы побегаем или введем другие чит-коды, увидим как они укладываются в памяти.
Посмотрим же, что использует этот буфер - попросим CE найти всех, кто использует данный адрес при вводе чит-кода
Сначала жмем ПКМ на адресе в главном окне CE и выбираемFind out what accesses this address
:Теперь переключимся в игру и введем любое слово, например
HELLOGHL
. Этим мы отсеим инструкции, которые используют наш адрес при обычном вводе. В окне появились следующие инструкции, мы будем их игнорировать:Теперь введем чит-код на создание автомобиля -
THELASTRIDE
. Внимательно следим за окном с инструкциями, т.к. в процессе ввода у меня появились еще несколько инструкций, которые используют адрес, но не влияют на появление машины. После ввода последней буквы видим следующую картину:Уже можно заметить несколько функций, выполняющихся один раз. Для того, чтобы отсеять лишние инструкции, попробуем ввести другой чит-код, не относящийся к созданию автомобилей, например
LEAVEMEALONE
:Ура, из предыдущих инструкций у нас осталось одна - инструкция
mov byte ptr [00A0F94A]
по адресу004AC84A
. Давайте выделим её и нажмем Show disassembler, чтобы увидеть, где она выполняется:Собственно мы видим некое условие, после которого в наш буфер записывает
0x20
, он же32
он же пробел и вызывается некая функцияgta-vc.exe+AE7C0
с аргументом AC. Заочно можно нас поздравить, потому что мы нашли нужную нам функцию, осталось только убедиться в этом.Попробуйте тем же способом найти чит-функцию для создания танка. Я её уже нашел, давайте посмотрим:
Видно, что функция находится немного в другом месте, но отличие состоит только в том, какой аргумент передается в функцию - push записывает в стек
A2
, вместоAC
. В остальном код условия идентичен и я предлагаю попробовать просто вызывать функциюgta-vc.exe+AE7C0
с разными параметрами.Для вызова функции мы будем использовать
AutoAssembe
и функциюcreatethread
.
Зайдем вAutoAssembler
(откроемMemory Viewer
, менюTools - AutoAssemble
) и набросаем следующий скрипт:[ENABLE] // код, который выполнится при включении скрипта // выделяем память под нашу функцию, хватит и 64 байта alloc(our_func, 64) // начало нашей функции // фактически просто копируем код из игры our_func: // записываем в стек AC, он же 172 push 000000AC // вызываем некую функцию call gta-vc.exe+AE7C0 // убираем из стека наш AC pop ecx // конец функции ret // создаем поток, который прыгнет на нашу our_func и выполнит код createthread(our_func) [DISABLE] // код, который выполнится при выключении скрипта // просто освобождаем память от нашей функции dealloc(our_func)
Добавим его в таблицу через
File - Assign to current cheat table
и можно закрывать окноAuto assemble
.Скрипт выделяет в памяти игры 64 байта для себя, после чего записывает туда инструкции, которые мы скопировали из оригинальной игры и просит createthread выполнить их.
Обратите внимание, что аргументы в данную функцию передаются как
push AC
. В оригинальной игре после call мы можем заметить инструкциюpop ecx
. Это значит, что функция не трогает стек и мы сам должны двигать его за ним (т.е. откатить pushAC
). Поэтому после функции выполняется pop и мы обязательно должны его скопировать, иначе ret будет пытаться вернуть поток не куда-то по адресу0xAA123456
, а по адресу0x000000AC
. Почемуpop
используетecx
? В данном случае - потому что левая пятка компилятора так решила и это ни на что не влияет (ecx
спокойно можно заменить наeax
и другие регистры).Ура, скрипт в нашей таблице, давайте же активируем его! Смотрим на результат и...:
Наш скрипт вызвал чит-функцию создания катафалка! Наверное вы уже догадались, что если мы заменим push AC на push A2 мы увидим заспаунившися танк?
Получается, что
AC
- это катафалк, аA2
- танк. ПоискавGTA Vice City Vehicle IDs
, мы найдем, что действительно,A2 = 162
- это Vehicle ID танка, аAC = 172
- катафалк. Попробуем указать свой ID, один из списка, например168
(такси). Передача аргумента превратится вpush #168
:Можно сказать, что наш скрипт работает! Осталось вынести ID модели как переменную:
[ENABLE] // код, который выполнится при включении скрипта // выделяем память под нашу функцию, хватит и 64 байта alloc(our_func, 64) // выделяем память под переменную alloc(our_vehicle_id, 4) // объявляем нашу our_func как переменную, // чтобы можно было вызывать createthread из другого скрипта registersymbol(our_func) // объявляем нашу our_vehicle_id как переменную, // чтобы можно было обращаться из таблицы registersymbol(our_vehicle_id) // записываем начальное занчение our_vehicle_id: // sabre turbo dd #206 // начало нашей функции // фактически просто копируем код из игры our_func: // записываем в стек наш ID машины push [our_vehicle_id] // вызываем некую функцию call gta-vc.exe+AE7C0 // убираем из стека наш AC pop ecx // конец функции ret [DISABLE] // код, который выполнится при выключении скрипта // удаляем информацию о функции dealloc(our_func) unregistersymbol(our_func) // удаляем информацию о переменной dealloc(our_vehicle_id) unregistersymbol(our_vehicle_id)
Я удалил из скрипта функцию вызова потока, чтобы переместить её в отдельный скрипт:
[ENABLE] createthread(our_func) [DISABLE]
Теперь можно добавить в таблицу адрес
our_vehicle_id
, в который после записи скрипта будет записано число206
(ID наикрутейшего Sabre Turbo).Это число отвечает за то, что мы передаем в чит-функцию и меня его мы будем менять ID заспаунившегося автомобиля:
Результат:
И о проблемах
Скрипт фактически вызывает чит-код, поэтому мы начинаем слыть читерами, рейтинг в игре уменьшается и т.д. Это довольно легко обходится, т.к. если мы провалимся в функцию
gta-vc.exe+AE7C0
, перед самымret
есть две инструкции -add [gta-vc.exe+5B4F94],000003E8
иmov byte ptr [gta-vc.exe+60FB37],01
. Первая накидывает очки читерства (они хранятся отдельно) и устанавливает флаг - "этот парень использовал коды". Если их занопить - игра никогда не узнает о ваших проделках.И вновь, потому что мы просто вызываем функцию чит-кода, она спаунит машины только на дорогу. Она находит ближайшую RoadPoint, прибавляет по оси Z несколько метров и спаунит авто. Поэтому создать авто прямо перед игроком так просто не получится.
Ну и основная проблема - это то, что в данной функции использует конструктор только для автомобилей. Он так же используется для вертолетов, но вот байки и лодки с ним заспаунить не получится (заспаунится, но крешится при посадке и выглядит, кхм...):
Спасибо @Imaginary за перенос статьи на форум