Пишем trainer для игры
Пишем trainer для игры
Тренер, трейнер (англ. "trainer") - программа, предназначенная для изменения обычного поведения игры (например, дающая игроку бесконечное количество жизней, патронов, бессмертие, быструю победу и т.п.) в реальном времени. Трейнеры наиболее полезны для игр, в которых не предусмотрены чит-коды.
Лично я считаю, что любая игра должна доставлять в первую очередь радость и глубокое удовлетворение, а не бурю негативных эмоций от того, что уже в сотый раз не можешь пройти очередного босса. Поэтому я всегда играю на самых легких уровнях с использованием всех доступных чит-кодов, трейнеров, прохождений и т.п. Но сегодня речь не об этом, а о том, как пишутся трейнеры.
Игра "Air Xonix"
В качестве "тренируемой" игрушки сегодня будет Air Xonix. Будем делать так, чтобы жизни в игре никогда не заканчивались. Игру сперва надо довести до ума, чтобы остался чистый исполняемый файл, после этого можно приступать к исследованиям.
Первый инструмент, который нам понадобится, это ArtMoney. Она умеет сканировать память для поиска каких-то определенных значений, отсеивать изменения, редактировать и замораживать значения ячеек памяти и т.п. Подробнее о возможностях ArtMoney прочитайте на офсайте. В принципе, вечные жизни в игре можно сделать средствами самой ArtMoney, но в данном случае для нас это будет только вспомогательный инструмент. И нам будет достаточно даже бесплатного варианта, который можно свободно скачать с офсайта и использовать без регистрации.
Ищем значение жизней в ArtMoney
Запускаем и начинаем игру, ставим на паузу во время игрового процесса, переключаем в ArtMoney и выбираем ее процесс по заголовку окна. Теперь нам надо найти значение жизней. Сперва это будет 4. Нажимаем кнопку "Искать", вводим значение "4" и выбираем тип значений для поиска.
Задаем тип искомых значений
По моему опыту чаще всего счетчики хранятся в целом виде или в 2 байтах (WORD), 4 байтах (DWORD) или 8 байтах (QWORD). Начинать поиск лучше с них, это значительно сократит диапазон поиска, особенно для малых значений. Найденных значений после первого поиска может быть (и будет!) очень много, а нам надо выбрать только нужные. Поэтому поиск разделяется на два этапа - поиск значений и отсев ненужных значений. В итоге получаем адреса, в которых находятся числа, и их можно изменить на нужные.
Отсеиваем изменившееся значение
Переключаемся на игру, снимаем ее с паузы, нарываемся на противника и теряем жизнь. Снова ставим на паузу, переключаемся в ArtMoney и ищем новое значение. Но на этот раз воспользуемся кнопкой "Отсеять". Повторяем процедуру отсеивания до тех пор, пока в списке не останется только одно значение. В нашем случае это будет адрес 0257DA10. Мы выяснили адрес ячейки памяти, в которой хранится значение жизней. Теперь нам надо выяснить, какой код в исполняемом файле отвечает за изменение этого значения.
Ставим в отладчике точку останова на запись в память
Запускаем отладчик и аттачимся к процессу игры. Переходим в окне дампа на адрес 0257DA10 и ставим точку останова на запись в память по этому адресу. Отпускаем игру и снова нарываемся на противника, чтобы потерять еще одну жизнь.
Точка останова сработала
Отладчик остановится на вышеуказанном коде. Он очень простой и понятный. В регистр ESI загружается значение жизней, уменьшается на 1 (команда DEC), затем записывается обратно. То есть, чтобы стать бессмертным, надо заNOPить команду уменьшения регистра.
Патчим файл в памяти вручную
Прямо в отладчике заменяем команду DEC ESI на NOP. Теперь, по идее, значение жизней не должно уменьшаться.
Убираем точку останова
Чтобы каждый раз не активировался отладчик, снимаем точку останова с ячейки памяти. Снимаем игру с паузы в отладчике, переключаемся на игровой процесс и снова нарываемся на противника. Но на этот раз мы видим, что значение жизней остается прежним, оно не уменьшается. А если подобрать бонусную жизнь, то значение увеличивается. Все, секрет бессмертия в игре Air Xonix разгадан.
Можно пропатчить исполняемый файл и жизни будут всегда вечными. Но это история для другой сказки. Мы будем делать трейнер, который будет патчить код игры в памяти. Первый, простейший вариант трейнера - это лоадер. Он запускает игру, сразу же патчит ее в памяти и завершает свою работу. Из продвинутых функций есть только проверка содержимого памяти перед патчем, потому что трейнер должен работать только с определенной версией игры. Трейнер должен находиться там же, где и исполняемый файл игры.
Code (Assembler) : Убрать нумерацию
- section '.data' data readable writeable
- ; Данные для патча
- life db 090h
- errt db 'Error',0 ; Заголовок сообщения об ошибке
- err1 db 'File not found: ' ; Сообщение об отсутствии файла
- fname db 'AirXonix.exe',0 ; Имя исполняемого файла
- err2 db 'Incorrect game version',0 ; Неправильная версия игры
- ; Данные для запуска процесса
- sinfo STARTUPINFO
- pinfo PROCESS_INFORMATION
- tmp dd ?
- ;--------------------------------------------------------------------
- section '.code' code readable executable
- start:
- ; Запустить процесс игры
- invoke CreateProcess,fname,0,NULL,NULL,NULL,\
- NORMAL_PRIORITY_CLASS,NULL,NULL,sinfo,pinfo
- or eax,eax
- jnz start_ok
- ; Невозможно запустить процесс
- invoke MessageBox,0,err1,errt,MB_OK
- jmp loc_exit
- start_ok:
- ; Прочитать 4 байта из места будущего патча
- invoke ReadProcessMemory,[pinfo.hProcess],00419680h,tmp,4,NULL
- ; Контрольное содержимое участка памяти
- cmp [tmp],09E0C14Eh
- ; Если все нормально, то пропатчить файл в памяти
- je patch_memory
- ; Иначе завершить процесс и вывести сообщение о неправильной версии игры
- invoke TerminateProcess,[pinfo.hProcess],0
- invoke MessageBox,0,err2,errt,MB_OK
- jmp loc_exit
- patch_memory:
- ; Записать NOP вместо DEC ESI
- invoke WriteProcessMemory,[pinfo.hProcess],00419680h,life,1,NULL
- loc_exit:
- invoke ExitProcess,0
Code (Assembler) : Убрать нумерацию
- section '.data' data readable writeable
- ; Структуры для работы привилегиями
- struct LUID
- lowPart dd ?
- HighPart dd ?
- ends
- struct LUID_AND_ATTRIBUTES
- pLuid LUID
- Attributes dd ?
- ends
- struct _TOKEN_PRIVILEGES
- PrivilegeCount dd ?
- Privileges LUID_AND_ATTRIBUTES
- ends
- ; Константы для работы привилегиями
- TOKEN_ADJUST_PRIVILEGES = 20h
- TOKEN_QUERY = 8h
- SE_PRIVILEGE_ENABLED = 2h
- ; Заголовок окна игры для поиска
- wTitle db 'Воздушный Ксоникс',0
- ; Данные для патча
- life db 090h
- errt db 'Error',0
- err1 db 'Can not open process',0
- err2 db 'Incorrect game version',0
- SE_DEBUG_NAME db 'SeDebugPrivilege',0
- tmp dd ?
- hProcess dd ?
- udtLUID LUID
- tkp _TOKEN_PRIVILEGES
- TTokenHd dd ?
- ;--------------------------------------------------
- section '.code' code readable executable
- start:
- ; Повысить привилегии процесса
- invoke GetCurrentProcess
- invoke OpenProcessToken,eax,TOKEN_ADJUST_PRIVILEGES+TOKEN_QUERY,TTokenHd
- or eax,eax
- jz loc_exit
- ; Получить привилегии процесса
- invoke LookupPrivilegeValue,NULL,SE_DEBUG_NAME,udtLUID
- or eax,eax
- jz loc_exit
- ; Повысить привилегии процесса
- mov [tkp.PrivilegeCount],1
- mov [tkp.Privileges.Attributes],SE_PRIVILEGE_ENABLED
- mov eax,[udtLUID.lowPart]
- mov [tkp.Privileges.pLuid.lowPart],eax
- mov eax,[udtLUID.HighPart]
- mov [tkp.Privileges.pLuid.HighPart],eax
- invoke AdjustTokenPrivileges,[TTokenHd],0,tkp,0,0,0
- find_window:
- ; Поиск окна запущенной игры
- invoke FindWindow,NULL,wTitle
- or eax,eax
- jnz open_process
- ; Пауза, чтобы не нагружать процессор
- invoke Sleep,1000
- jmp find_window
- open_process:
- ; Получить родительский процесс окна
- invoke GetWindowThreadProcessId,eax,tmp
- ; Открыть процесс
- invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,[tmp]
- or eax,eax
- jnz @f
- ; Невозможно открыть процесс игры для записи
- invoke MessageBox,0,err1,errt,MB_OK
- jmp loc_exit
- @@:
- mov [hProcess],eax
- ; Прочитать 4 байта из места будущего патча
- invoke ReadProcessMemory,[hProcess],00419680h,tmp,4,NULL
- ; Если все нормально, то пропатчить файл в памяти
- cmp [tmp],09E0C14Eh
- je patch_memory
- ; Иначе вывести сообщение о неправильной версии игры
- invoke MessageBox,0,err2,errt,MB_OK
- jmp loc_exit
- patch_memory:
- ; Записать NOP вместо DEC ESI
- invoke WriteProcessMemory,[hProcess],00419680h,life,1,NULL
- loc_exit:
- invoke ExitProcess,0
Code (Assembler) : Убрать нумерацию
- section '.data' data readable writeable
- ; Данные для патча
- life db 99
- errt db 'Error',0
- err1 db 'File not found: '
- fname db 'AirXonix.exe',0
- err2 db 'Incorrect game version',0
- ; Данные для запуска процесса
- sinfo STARTUPINFO
- pinfo PROCESS_INFORMATION
- tmp dd ?
- ;--------------------------------------------------
- section '.code' code readable executable
- start:
- ; Запустить процесс игры
- invoke CreateProcess,fname,0,NULL,NULL,NULL,\
- NORMAL_PRIORITY_CLASS,NULL,NULL,sinfo,pinfo
- or eax,eax
- jnz start_ok
- ; Невозможно запустить процесс
- invoke MessageBox,0,err1,errt,MB_OK
- jmp loc_exit
- start_ok:
- ; Прочитать 4 байта из места будущего патча
- invoke ReadProcessMemory,[pinfo.hProcess],00419680h,tmp,4,NULL
- ; Контрольное содержимое участка памяти
- cmp [tmp],09E0C14Eh
- ; Если все нормально, то пропатчить файл в памяти
- je patch_memory
- ; Иначе завершить процесс и вывести сообщение о неправильной версии игры
- invoke TerminateProcess,[pinfo.hProcess],0
- invoke MessageBox,0,err2,errt,MB_OK
- jmp loc_exit
- patch_memory:
- ; Записать 99 жизней в ячейку памяти
- invoke WriteProcessMemory,[pinfo.hProcess],0257DA10h,life,1,NULL
- ; Небольшая пауза
- invoke Sleep,500
- ; Процесс еще активен?
- invoke GetExitCodeProcess,[pinfo.hProcess],tmp
- cmp [tmp],STILL_ACTIVE
- ; Продолжаем патчить значение в памяти
- je patch_memory
- loc_exit:
- invoke ExitProcess,0
В приложении примеры всех трех вариантов трейнеров с исходными текстами, описанные в статье. Они протестированы и работают с игрой Air Xonix версии 1.45.
Просмотров: 12292 | Комментариев: 12
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(14.12.2022 в 20:49):
Так это ж получается типичный инжект в процесс. Материалов масса на любой вкус, как и исходников на чем угодно https://yandex.ru/search/?text...%D1%81%D1%81
master
(14.12.2022 в 20:30):
Доброго времени суток!
Подскажите пожалуйста, как можно сделать подобный вариант патча и возможно ли сделать такое вообще?
Берем какой-нибудь адрес, например 10010DFA - и вместо текущей команды - переадресовываем ее на свою метку
В метке, которую добавил - можно выполнить тот-же самый код. Который вырезали.
.text:10010DFA sbb eax, eax - здесь наша метка METKA_TEST, в которой можно выполнить в
.text:10010DFC neg eax
.text:10010DFE pop ecx
.text:10010DFF dec eax
.text:10010E00 retn
METKA_TEST:
sbb eax, eax
neg eax
pop ecx
dec eax
retn
По адресу 10010DFA врезаемся, захватывая команду
METKA_TEST2:
sub eax, eax ; делаем то, что было вырезано
inc eax ; пишем свою команду
METKA_TEST_EXIT: ; возврат на адрес 10010DFE и продолжить все, что идет после него.
Подскажите пожалуйста, как можно сделать подобный вариант патча и возможно ли сделать такое вообще?
Берем какой-нибудь адрес, например 10010DFA - и вместо текущей команды - переадресовываем ее на свою метку
В метке, которую добавил - можно выполнить тот-же самый код. Который вырезали.
.text:10010DFA sbb eax, eax - здесь наша метка METKA_TEST, в которой можно выполнить в
.text:10010DFC neg eax
.text:10010DFE pop ecx
.text:10010DFF dec eax
.text:10010E00 retn
METKA_TEST:
sbb eax, eax
neg eax
pop ecx
dec eax
retn
По адресу 10010DFA врезаемся, захватывая команду
METKA_TEST2:
sub eax, eax ; делаем то, что было вырезано
inc eax ; пишем свою команду
METKA_TEST_EXIT: ; возврат на адрес 10010DFE и продолжить все, что идет после него.
ManHunter
(27.08.2014 в 02:31):
Без прав сложнее получить доступ к памяти процесса игры.
анон
(27.08.2014 в 02:25):
а подскажи пожалуйста, есть трейнеры к одной и той же игре, но один работает только с правами администратора, а другой прав не требует, причём требующих прав трейнеров вроде большинство, почему так? без прав сложнее создавать?
Exit
(29.04.2014 в 14:47):
ManHunter, спасибо за статью!
P.S. в сети попадалась интересная софтина - Cheat Engine - заточена именно под создание читов. Есть даже встроенный простой отладчик! ))
http://cheatengine.org/aboutce.php
а на ютубе, есть ролики, на русском языке, для начинающих... ))
а автор роликов ведет где-то блог...
Особенностью данной софтины является то, что на выходе она может делать готовый трейнер + в проге есть интерактивная справка, где учишься искать и патчить ))
В общем, мечта начинающего кулхацкера ))
P.S. в сети попадалась интересная софтина - Cheat Engine - заточена именно под создание читов. Есть даже встроенный простой отладчик! ))
http://cheatengine.org/aboutce.php
а на ютубе, есть ролики, на русском языке, для начинающих... ))
а автор роликов ведет где-то блог...
Особенностью данной софтины является то, что на выходе она может делать готовый трейнер + в проге есть интерактивная справка, где учишься искать и патчить ))
В общем, мечта начинающего кулхацкера ))
Zhelezyaka
(27.04.2014 в 12:56):
Вот это сюрприз :) Игрушки - наше всё, спасибо.
kanisserik
(26.04.2014 в 21:31):
morgot, проще надо быть. Как правило, данные об игровых балансах и ценностях хранятся на сервере. Я же имел в виду характеристики самого игрока, например, темп стрельбы или скорость бега.
morgot
(26.04.2014 в 20:14):
kanisserik, учите матчасть. Если данные онлайн-игры хранятся на сервере, т.е. где-то в базе данных в датацентре в другой стране, то ес-но что локально вы ее никак не пропатчите. Если же у онлайн игры есть приложение-клиент, то можно попробовать.
Григорий
(26.04.2014 в 17:50):
Хот сам не занимаюсь взломом игр и вряд ли буду, но много полезного узнал. Спасибо!)
Макс
(26.04.2014 в 10:07):
Да! Пропатчить бы Контрстрайк , чтобы хёлспоинты не уменьшались, цены бы не было такой программе :)
ManHunter
(26.04.2014 в 06:45):
Я не знаю что такое онлайновые игры.
kanisserik
(26.04.2014 в 01:15):
Это конечно здорово. А как быть с онлайновыми играми, в которых есть защита от читов?
Добавить комментарий
Заполните форму для добавления комментария