Blog. Just Blog

Пишем trainer для игры

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Пишем trainer для игры
Пишем trainer для игры


Тренер, трейнер (англ. "trainer") - программа, предназначенная для изменения обычного поведения игры (например, дающая игроку бесконечное количество жизней, патронов, бессмертие, быструю победу и т.п.) в реальном времени. Трейнеры наиболее полезны для игр, в которых не предусмотрены чит-коды.


Лично я считаю, что любая игра должна доставлять в первую очередь радость и глубокое удовлетворение, а не бурю негативных эмоций от того, что уже в сотый раз не можешь пройти очередного босса. Поэтому я всегда играю на самых легких уровнях с использованием всех доступных чит-кодов, трейнеров, прохождений и т.п. Но сегодня речь не об этом, а о том, как пишутся трейнеры.

Игра "Air Xonix"
Игра "Air Xonix"

В качестве "тренируемой" игрушки сегодня будет Air Xonix. Будем делать так, чтобы жизни в игре никогда не заканчивались. Игру сперва надо довести до ума, чтобы остался чистый исполняемый файл, после этого можно приступать к исследованиям.

Первый инструмент, который нам понадобится, это ArtMoney. Она умеет сканировать память для поиска каких-то определенных значений, отсеивать изменения, редактировать и замораживать значения ячеек памяти и т.п. Подробнее о возможностях 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 разгадан.

Можно пропатчить исполняемый файл и жизни будут всегда вечными. Но это история для другой сказки. Мы будем делать трейнер, который будет патчить код игры в памяти. Первый, простейший вариант трейнера - это лоадер. Он запускает игру, сразу же патчит ее в памяти и завершает свою работу. Из продвинутых функций есть только проверка содержимого памяти перед патчем, потому что трейнер должен работать только с определенной версией игры. Трейнер должен находиться там же, где и исполняемый файл игры.
  1. section '.data' data readable writeable
  2.  
  3. ; Данные для патча
  4. life    db 090h
  5.  
  6. errt    db 'Error',0                    ; Заголовок сообщения об ошибке
  7. err1    db 'File not found: '           ; Сообщение об отсутствии файла
  8. fname   db 'AirXonix.exe',0             ; Имя исполняемого файла
  9. err2    db 'Incorrect game version',0   ; Неправильная версия игры
  10.  
  11. ; Данные для запуска процесса
  12. sinfo   STARTUPINFO
  13. pinfo   PROCESS_INFORMATION
  14.  
  15. tmp     dd ?
  16.  
  17. ;--------------------------------------------------------------------
  18.  
  19. section '.code' code readable executable
  20.  
  21. start:
  22.         ; Запустить процесс игры
  23.         invoke  CreateProcess,fname,0,NULL,NULL,NULL,\
  24.                 NORMAL_PRIORITY_CLASS,NULL,NULL,sinfo,pinfo
  25.         or      eax,eax
  26.         jnz     start_ok
  27.  
  28.         ; Невозможно запустить процесс
  29.         invoke  MessageBox,0,err1,errt,MB_OK
  30.         jmp     loc_exit
  31.  
  32. start_ok:
  33.         ; Прочитать 4 байта из места будущего патча
  34.         invoke  ReadProcessMemory,[pinfo.hProcess],00419680h,tmp,4,NULL
  35.         ; Контрольное содержимое участка памяти
  36.         cmp     [tmp],09E0C14Eh
  37.         ; Если все нормально, то пропатчить файл в памяти
  38.         je      patch_memory
  39.  
  40.         ; Иначе завершить процесс и вывести сообщение о неправильной версии игры
  41.         invoke  TerminateProcess,[pinfo.hProcess],0
  42.         invoke  MessageBox,0,err2,errt,MB_OK
  43.         jmp     loc_exit
  44.  
  45. patch_memory:
  46.         ; Записать NOP вместо DEC ESI
  47.         invoke  WriteProcessMemory,[pinfo.hProcess],00419680h,life,1,NULL
  48.  
  49. loc_exit:
  50.         invoke  ExitProcess,0
Более продвинутый вариант трейнера, который используется в большинстве случаев. Он ожидает появления окна игры, открывает процесс игры и патчит в нем нужные байты. От предыдущего трейнера этот отличается тем, что он может быть запущен в любой момент независимо до запуска игры или после запуска игры и может находиться в любой папке. Для такого трейнера не обязательно отвязывать игру от враппера, он может работать с оригинальной немодифицированной игрой. Недостатком этого способа является то, что для работы такого трейнера требуется повышение его привилегий, чтобы он мог работать с памятью других процессов.
  1. section '.data' data readable writeable
  2.  
  3. ; Структуры для работы привилегиями
  4. struct LUID
  5.     lowPart             dd ?
  6.     HighPart            dd ?
  7. ends
  8.  
  9. struct LUID_AND_ATTRIBUTES
  10.     pLuid               LUID
  11.     Attributes          dd ?
  12. ends
  13.  
  14. struct _TOKEN_PRIVILEGES
  15.     PrivilegeCount      dd ?
  16.     Privileges          LUID_AND_ATTRIBUTES
  17. ends
  18.  
  19. ; Константы для работы привилегиями
  20. TOKEN_ADJUST_PRIVILEGES = 20h
  21. TOKEN_QUERY             = 8h
  22. SE_PRIVILEGE_ENABLED    = 2h
  23.  
  24. ; Заголовок окна игры для поиска
  25. wTitle  db 'Воздушный Ксоникс',0
  26.  
  27. ; Данные для патча
  28. life     db 090h
  29.  
  30. errt     db 'Error',0
  31. err1     db 'Can not open process',0
  32. err2     db 'Incorrect game version',0
  33.  
  34. SE_DEBUG_NAME db 'SeDebugPrivilege',0
  35.  
  36. tmp      dd ?
  37. hProcess dd ?
  38.  
  39. udtLUID  LUID
  40. tkp      _TOKEN_PRIVILEGES
  41. TTokenHd dd ?
  42.  
  43. ;--------------------------------------------------
  44.  
  45. section '.code' code readable executable
  46.  
  47. start:
  48.         ; Повысить привилегии процесса
  49.         invoke  GetCurrentProcess
  50.         invoke  OpenProcessToken,eax,TOKEN_ADJUST_PRIVILEGES+TOKEN_QUERY,TTokenHd
  51.         or      eax,eax
  52.         jz      loc_exit
  53.  
  54.         ; Получить привилегии процесса
  55.         invoke  LookupPrivilegeValue,NULL,SE_DEBUG_NAME,udtLUID
  56.         or      eax,eax
  57.         jz      loc_exit
  58.  
  59.         ; Повысить привилегии процесса
  60.         mov     [tkp.PrivilegeCount],1
  61.         mov     [tkp.Privileges.Attributes],SE_PRIVILEGE_ENABLED
  62.         mov     eax,[udtLUID.lowPart]
  63.         mov     [tkp.Privileges.pLuid.lowPart],eax
  64.         mov     eax,[udtLUID.HighPart]
  65.         mov     [tkp.Privileges.pLuid.HighPart],eax
  66.         invoke  AdjustTokenPrivileges,[TTokenHd],0,tkp,0,0,0
  67.  
  68. find_window:
  69.         ; Поиск окна запущенной игры
  70.         invoke  FindWindow,NULL,wTitle
  71.         or      eax,eax
  72.         jnz     open_process
  73.  
  74.         ; Пауза, чтобы не нагружать процессор
  75.         invoke  Sleep,1000
  76.         jmp     find_window
  77.  
  78. open_process:
  79.         ; Получить родительский процесс окна
  80.         invoke  GetWindowThreadProcessId,eax,tmp
  81.  
  82.         ; Открыть процесс
  83.         invoke  OpenProcess,PROCESS_ALL_ACCESS,FALSE,[tmp]
  84.         or      eax,eax
  85.         jnz     @f
  86.  
  87.         ; Невозможно открыть процесс игры для записи
  88.         invoke  MessageBox,0,err1,errt,MB_OK
  89.         jmp     loc_exit
  90. @@:
  91.         mov     [hProcess],eax
  92.  
  93.         ; Прочитать 4 байта из места будущего патча
  94.         invoke  ReadProcessMemory,[hProcess],00419680h,tmp,4,NULL
  95.         ; Если все нормально, то пропатчить файл в памяти
  96.         cmp     [tmp],09E0C14Eh
  97.         je      patch_memory
  98.  
  99.         ; Иначе вывести сообщение о неправильной версии игры
  100.         invoke  MessageBox,0,err2,errt,MB_OK
  101.         jmp     loc_exit
  102.  
  103. patch_memory:
  104.         ; Записать NOP вместо DEC ESI
  105.         invoke  WriteProcessMemory,[hProcess],00419680h,life,1,NULL
  106.  
  107. loc_exit:
  108.         invoke  ExitProcess,0
Третий вариант трейнера модифицирует не исполняемый код, а замораживает нужную ячейку памяти, постоянно записывая в нее значение. Этот способ можно использовать в тех случаях, когда, например, увеличение и уменьшение нужных значений выполняется одним и тем же кодом или когда процесс игры контролирует свою целостность. В нашем случае все просто, адрес ячейки с количеством жизней постоянный, запускаем игру и с интервалом в 0,5 секунды пишем число 99 в ячейку с количеством жизней. Примерно по этому же принципу работает заморозка значений в программе ArtMoney. Если процесс игры завершится, то и трейнер должен завершить свою работу.
  1. section '.data' data readable writeable
  2.  
  3. ; Данные для патча
  4. life    db 99
  5.  
  6. errt    db 'Error',0
  7. err1    db 'File not found: '
  8. fname   db 'AirXonix.exe',0
  9. err2    db 'Incorrect game version',0
  10.  
  11. ; Данные для запуска процесса
  12. sinfo   STARTUPINFO
  13. pinfo   PROCESS_INFORMATION
  14.  
  15. tmp     dd ?
  16.  
  17. ;--------------------------------------------------
  18.  
  19. section '.code' code readable executable
  20.  
  21. start:
  22.         ; Запустить процесс игры
  23.         invoke  CreateProcess,fname,0,NULL,NULL,NULL,\
  24.                 NORMAL_PRIORITY_CLASS,NULL,NULL,sinfo,pinfo
  25.         or      eax,eax
  26.         jnz     start_ok
  27.  
  28.         ; Невозможно запустить процесс
  29.         invoke  MessageBox,0,err1,errt,MB_OK
  30.         jmp     loc_exit
  31.  
  32. start_ok:
  33.         ; Прочитать 4 байта из места будущего патча
  34.         invoke  ReadProcessMemory,[pinfo.hProcess],00419680h,tmp,4,NULL
  35.         ; Контрольное содержимое участка памяти
  36.         cmp     [tmp],09E0C14Eh
  37.         ; Если все нормально, то пропатчить файл в памяти
  38.         je      patch_memory
  39.  
  40.         ; Иначе завершить процесс и вывести сообщение о неправильной версии игры
  41.         invoke  TerminateProcess,[pinfo.hProcess],0
  42.         invoke  MessageBox,0,err2,errt,MB_OK
  43.         jmp     loc_exit
  44.  
  45. patch_memory:
  46.         ; Записать 99 жизней в ячейку памяти
  47.         invoke  WriteProcessMemory,[pinfo.hProcess],0257DA10h,life,1,NULL
  48.  
  49.         ; Небольшая пауза
  50.         invoke  Sleep,500
  51.  
  52.         ; Процесс еще активен?
  53.         invoke  GetExitCodeProcess,[pinfo.hProcess],tmp
  54.         cmp     [tmp],STILL_ACTIVE
  55.         ; Продолжаем патчить значение в памяти
  56.         je      patch_memory
  57.  
  58. loc_exit:
  59.         invoke  ExitProcess,0
Это всего лишь минимальные заготовки трейнеров, для понимания процесса их работы. Для полноценных релизов их, конечно же, надо доукомплектовать диалоговыми окнами, красивой графикой и музыкой, добавить включение-выключение по горячим клавишам и всякое такое. Но это уже вы можете сделать самостоятельно. Да и "тренировать" можно не только игры, но и другие программы, тут главное освоить принцип взаимодействия с памятью сторонних процессов.

В приложении примеры всех трех вариантов трейнеров с исходными текстами, описанные в статье. Они протестированы и работают с игрой Air Xonix версии 1.45.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

Trainer.Demo.zip (6,834 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 12054 | Комментариев: 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 и продолжить все, что идет после него.
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
а на ютубе, есть ролики, на русском языке, для начинающих... ))
а автор роликов ведет где-то блог...
Особенностью данной софтины является то, что на выходе она может делать готовый трейнер + в проге есть интерактивная справка, где учишься искать и патчить ))
В общем, мечта начинающего кулхацкера ))
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):
Это конечно здорово. А как быть с онлайновыми играми, в которых есть защита от читов?

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

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.09 сек. / MySQL: 2 (0.0078 сек.) / Память: 4.5 Mb
Наверх