Пишемо trainer для гри
Пишемо trainer для гри
Особисто я вважаю, що будь-яка гра повинна доставляти в першу чергу радість і глибоке задоволення, а не бурю негативних емоцій від того, що вже всоте не можеш пройти чергового боса. Тому я завжди граю на найлегших рівнях із використанням усіх доступних чит-кодів, трейнерів, проходжень тощо. Але сьогодні не про це, а про те, як пишуться трейнери.
Гра "Air Xonix"
Як "тренована" іграшка сьогодні буде Air Xonix. Робитимемо так, щоб життя в грі ніколи не закінчувалося. Гра спочатку треба довести до розуму, щоб залишився чистий виконуваний файл, після цього можна приступати до досліджень.
Шукаємо значення життів в ArtMoney
Запускаємо і починаємо гру, ставимо на паузу під час ігрового процесу, перемикаємо в ArtMoney і вибираємо її за заголовком вікна. Тепер нам треба знайти значення життя. Спочатку це буде 4. Натискаємо кнопку "Шукати", вводимо значення "4" і вибираємо тип значень для пошуку.
Задаємо тип шуканих значень
Відсіюємо значення, що змінилося
Ставимо у відладчику точку зупинки на запис на згадку
Точка зупинки спрацювала
Відладчик зупиниться на вказаному вище коді. Він дуже простий та зрозумілий. У регістр ESI завантажується значення життя, зменшується на 1 (команда DEC), потім записується назад. Тобто, щоб стати безсмертним, треба записати команду зменшення регістру.
Патчим файл у пам'яті вручну
Прямо у відладчику замінюємо команду DEC ESI на NOP. Тепер, за ідеєю, значення життя не повинно зменшуватися.
Прибираємо точку зупинки
Щоб щоразу не активувався відладчик, знімаємо точку зупинки з комірки пам'яті. Знімаємо гру з паузи у відладчику, перемикаємось на ігровийпроцес і знову нариваємося на супротивника. Але цього разу ми бачимо, що значення життів залишається незмінним, воно не зменшується. А якщо підібрати бонусне життя, то значення зростає. Все, секрет безсмертя у грі Air Xonix розгаданий.
Можна пропатчити виконуваний файл та життя будуть завжди вічними. Але це історія для іншої казки. Ми будемо робити трейнер, який патчитиме код гри в пам'яті. Перший, найпростіший варіант трейнера – це лоадер. Він запускає гру, одразу ж патчить її в пам'яті та завершує свою роботу. З просунутих функцій є лише перевірка вмісту пам'яті перед патчем, тому що трейнер повинен працювати лише з певною версією гри. Трейнер повинен знаходитися там же, де виконуваний файл гри.
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
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
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
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
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.