Иг­рать в игры любят все, но это гораз­до инте­рес­нее, ког­да у тебя име­ется нес­конча­емый запас пат­ронов и здо­ровья. Что­бы обза­вес­тись и тем и дру­гим, мож­но погуг­лить читы и трей­неры для тво­ей любимой игры. Но как быть, если их еще не раз­работа­ли? Написать самому! Обла­дая навыка­ми реверс‑инжи­нирин­га и прог­рамми­рова­ния, сде­лать это нам­ного про­ще, чем кажет­ся.
 

Выбор игры

Для начала опре­делим­ся с игрой. Мой выбор пал на Hyper Light Drifter (далее HLD). Если ты пла­ниру­ешь поэк­спе­римен­тировать с ком­мерчес­кой игрой, обра­ти вни­мание на сайт pcgamingwiki, а так­же на игры с откры­тым исходным кодом.

warning

Так как для написа­ния этой статьи я буду исполь­зовать ком­мерчес­кую игру, мне нуж­но удос­товерить­ся, что лицен­зион­ное сог­лашение (EULA) поз­воля­ет это делать.

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

EULA HLD
EULA HLD
 

Поиск значений

Для поис­ка зна­чений, которые будет изме­нять чит, мы ста­нем исполь­зовать Cheat Engine (далее CE).

За­пус­тим игру и в нас­трой­ках игры выберем окон­ный режим — нам нуж­но, что­бы на экра­не помеща­лось еще что‑то, кро­ме игры.

Оконный режим
Окон­ный режим

Как видим, в окон­ном режиме отсутс­тву­ет панель заголов­ка, с помощью которой мы мог­ли бы перетас­кивать окно игры по экра­ну. Что­бы испра­вить эту неп­рият­ность, откро­ем отладчик x64dbg, а имен­но его 32-бит­ную вер­сию (x32dbg) и запус­тим под ним HLD.

Пос­тавим брейк‑пой­нты на фун­кции CreateWindowExA и CreateWindowExW, которые отве­чают за соз­дание окна. Най­ти их мож­но на вклад­ке Symbols, выб­рав биб­лиоте­ку user32.dll.

Вкладка символов
Вклад­ка сим­волов

Ви­дим, что наше окно соз­дает­ся с парамет­ром dwStyle, име­ющим зна­чение WS_POPUP = 0x80000000.

Значение параметра dwStyle, равное WS_POPUP
Зна­чение парамет­ра dwStyle, рав­ное WS_POPUP

По­меня­ем это зна­чение на WS_OVERLAPPED = 0x00000000.

Параметр dwStyle, измененный на WS_OVERLAPPED
Па­раметр dwStyle, изме­нен­ный на WS_OVERLAPPED

И вот резуль­тат: теперь мы можем переме­щать окно.

Оконный режим с панелью заголовка окна
Окон­ный режим с панелью заголов­ка окна

Пос­ле того как мы нас­тро­или окно игры с помощью отладчи­ка, ненадол­го отло­жим его. Что­бы най­ти нуж­ные нам зна­чения в Cheat Engine, раз­берем­ся с теорией.

 

Что такое статический адрес

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

Ста­тичес­кие адре­са ука­зыва­ются в фор­мате [module+offset]. Нап­ример, в library.dll мы мог­ли обна­ружить зна­чение по адре­су 0x700004C0 (base = 0x70000000, offset = 0x4C0). Пос­коль­ку library.dll может переме­щать­ся и ее базовый адрес заг­рузки будет менять­ся, что­бы получить дос­туп к нашему зна­чению, мы не исполь­зуем этот адрес нап­рямую. Вмес­то это­го возь­мем адрес [library.dll + 0x4C0]. Сле­дова­тель­но, ког­да library.dll заг­ружа­ется по базово­му адре­су 0x10000000, [library.dll + 0x4C0] дает нам 0x100004C0 и у нас появит­ся дос­туп к нашему зна­чению.

Ес­ли же перемен­ная локаль­ная, то искать нуж­но в сте­ке. Для это­го получа­ем TebBaseAddress опре­делен­ного потока, а затем вто­рой ука­затель из этой струк­туры (FS:[0x04] или GS:[0x08], в зависи­мос­ти от раз­ряднос­ти про­цес­са), которая содер­жит вер­шину сте­ка. TebBasePointer может быть получен с помощью NtQueryInformationThread (если это 64-бит­ный про­цесс) или же с помощью Wow64GetThreadSelectorEntry (если это 32-бит­ный про­цесс в 64-бит­ной сис­теме).

 

Поиск показателей здоровья

За­пус­каем Cheat Engine и под­клю­чаем­ся к про­цес­су игры.

Подключение к процессу игры
Под­клю­чение к про­цес­су игры

Так как мы не зна­ем, в каком типе хра­нит­ся показа­тель здо­ровья, выс­тавля­ем сле­дующие парамет­ры для пер­вого ска­ниро­вания.

Первое сканирование
Пер­вое ска­ниро­вание

Да­лее про­дол­жаем ска­ниро­вание, не забывая при этом терять hp (показа­тель здо­ровья) в игре. Дела­ем мы это для того, что­бы отсле­живать изме­нения зна­чения hp в памяти игры через CE, а так­же умень­шать зна­чение в поис­ке для сле­дующих ска­ниро­ваний. Делать мы это будем до тех пор, пока не будет дос­тигну­то адек­ватное количес­тво зна­чений в окне CE. Адек­ватное количес­тво зна­чений в дан­ном слу­чае — это такое количес­тво адре­сов, про­вер­ка которых зай­мет мак­симум минут пять.

Найденные адреса и их значения
Най­ден­ные адре­са и их зна­чения

Мне приг­лянулись вот эти два адре­са, которые я добавил в ниж­нее окно двой­ным щел­чком мыши на них. Приг­лянулись они мне в пер­вую оче­редь потому, что зна­чения по этим адре­сам сре­ди всех осталь­ных име­ют наиболь­ший тип — double. Всег­да нуж­но про­верять от боль­шего типа к мень­шему. То есть сна­чала про­веря­ем адре­са, хра­нящие тип double, затем float, пос­ле integer и так далее. Более под­робно о раз­мере типов дан­ных мож­но про­читать в до­кумен­тации Microsoft.

Добавленные адреса
До­бав­ленные адре­са

Ес­ли мы поменя­ем зна­чение по адре­су 0x36501940, то на экра­не появит­ся полоса здо­ровья, но его количес­тво не поменя­ется.

Индикатор hp
Ин­дикатор hp

Ес­ли теперь мы поменя­ем зна­чение по адре­су 0x36501A30, то на экра­не появит­ся полоса hp и зна­чение изме­нит­ся. Это зна­чит, что мы наш­ли адрес, в котором хра­нит­ся зна­чение здо­ровья в игре. Зна­чение хра­нит­ся в фор­мате double (стан­дарт IEEE 754).

Изменение hp
Из­менение hp

Да­дим наз­вание най­ден­ным нами адре­сам: hp_bar и hp соот­ветс­твен­но. Одна­ко, как я уже рас­ска­зывал в раз­деле, пос­вящен­ном ста­тичес­ким адре­сам, най­ден­ный нами адрес будет бес­полезен пос­ле того, как мы вый­дем в меню или переза­пус­тим игру.

 

Поиск статического адреса для индикатора здоровья

Для даль­нейше­го поис­ка ста­тичес­кого адре­са вер­немся к отладчи­ку. В окне дам­па перехо­дим по ранее получен­ному адре­су 0x36501A30, в котором хра­нит­ся зна­чение hp.

Значение по адресу 0x36501A30 в окне дампа
Зна­чение по адре­су 0x36501A30 в окне дам­па

Ста­вим по адре­су 0x36501A34 аппа­рат­ный брейк‑пой­нт на запись и теря­ем в игре здо­ровье. Брейк‑пой­нт сра­баты­вает, и мы видим, что новое зна­чение hp берет­ся из регис­тра EDI. Это зна­чение явля­ется пер­вым парамет­ром текущей фун­кции.

Вый­дя из этой фун­кции, прос­ледим, отку­да она получа­ет свой пер­вый параметр. Мы уви­дим, что переда­ваемый параметр — это воз­вра­щаемое зна­чение фун­кции по адре­су 0x003EFCE9.

Пос­тавим брейк‑пой­нт на вызов фун­кции по адре­су 0x003EFCE9, а даль­ше про­дол­жим отладку, пока не оста­новим­ся на ее вызове. Зай­дя внутрь фун­кции, выпол­няем ее до кон­ца. Как толь­ко мы дос­тигнем адре­са 0x00F88E19, мы уви­дим, что регистр EAX хра­нит адрес зна­чения hp. Оче­вид­но, что в этой фун­кции про­исхо­дит дос­туп к нашему адре­су через ариф­метику с ука­зате­лями для струк­тур, а имен­но через при­бав­ление к ука­зате­лю сме­щений и даль­нейше­го его разыме­нова­ния. Более под­робно об этом мож­но про­читать здесь. Нам нуж­но будет пов­торно прой­тись по этой фун­кции, что­бы узнать, через какой адрес и сме­щения она получа­ет адрес зна­чения hp.

Пос­ле того как мы узна­ли адрес 0x353F9BB0, из которо­го получа­ется адрес зна­чения hp, начина­ем выходить из фун­кций. При этом вни­матель­но отсле­жива­ем, что переда­ется им в качес­тве парамет­ров. Спус­тя пару выходов мы нат­кнем­ся на сле­дующее.

Мы наш­ли ста­тичес­кий адрес! Если пос­мотреть его рас­положе­ние в памяти, он находит­ся в сек­ции .data.

Зная все сме­щения, добавим их в CE, нажав Add Address Manually.

 

Поиск значения числа патронов

Те­перь прис­тупим к поис­ку зна­чения чис­ла пат­ронов (ammo). Пер­вое ска­ниро­вание дела­ем с такими же парамет­рами поис­ка, как ког­да мы иска­ли здо­ровье.

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

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

 

Поиск статического адреса для ammo

Мы понима­ем, что показа­ния инди­като­ров в игре всег­да срав­нива­ются с фак­тичес­кими. Если одна из полос показы­вает не то, что нуж­но, ее дли­на изме­няет­ся. Поэто­му воз­вра­щаем­ся к отладчи­ку и начина­ем с аппа­рат­ного брейк‑пой­нта на запись по адре­су 0x365014С4. Как видим по ком­мента­риям, эта фун­кция уже нам встре­чалась.

По ана­логии с поис­ком hp, выходим из фун­кции.

Так как мы уже зна­ем, что инди­катор дол­жен получать зна­чение где‑то рань­ше, нам при­дет­ся про­лис­тать окно дизас­сем­бле­ра выше, пока мы не уви­дим фун­кцию, пред­положи­тель­но получа­ющую фак­тичес­кое зна­чение ammo.

Мы видим, что в этой фун­кции мы уже были, а это зна­чит, что она тоже получа­ет зна­чение, но уже ammo — 365014E0. Толь­ко какое‑то оно стран­ное.

До­бавив это «стран­ное» зна­чение в Cheat Engine, а потом изме­нив его, к при­меру, на 100, мы уви­дим, что на экра­не появит­ся инди­катор пат­ронов и его зна­чение поменя­ется. Зна­чит, мы наш­ли адрес, в котором хра­нит­ся зна­чение ammo в игре.

Зная все сме­щения от ста­тичес­кого адре­са к адре­су зна­чений ammo, добавим их в CE, нажав Add Address Manually.

info

Ско­рее все­го, боеп­рипасы в HLD пред­став­ляют собой заряд энер­гии и поэто­му хра­нят­ся в про­цен­тах, ведь при их поис­ке через отладчик мож­но было уви­деть стро­ки, содер­жащие сло­во energy. Которое намека­ет на то, как будет выг­лядеть зна­чение в памяти. Нап­ример, игра от одной небезыз­вес­тной поль­ской ком­пании хра­нила пат­роны в памяти вмес­те, а для игро­ка показы­вала раз­дель­но: как рожок, так и количес­тво оставших­ся пат­ронов, поэто­му при их поис­ке не уда­валось най­ти каж­дое из зна­чений, а нуж­но было искать их сум­му.

 

Проверка полученного статического адреса

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

 

Проверка для HP

Так выг­лядит наша cheat table для hp.

Таблица до выхода в меню / перезапуска игры
Таб­лица до выхода в меню / переза­пус­ка игры

А вот так она выг­лядит пос­ле переза­пус­ка игры.

Таблица после запуска игрового процесса
Таб­лица пос­ле запус­ка игро­вого про­цес­са
 

Проверка для ammo

Так выг­лядит наша cheat table для ammo.

Таблица до выхода в меню / перезапуска игры
Таб­лица до выхода в меню / переза­пус­ка игры

А вот так она выг­лядит пос­ле переза­пус­ка игры.

Таблица после запуска игрового процесса
Таб­лица пос­ле запус­ка игро­вого про­цес­са
 

Как будет выглядеть наш указатель в C++

В нашем чите дос­туп к най­ден­ным адре­сам зна­чений будет таким.

static_addr = (DWORD)GetModuleHandle(0);
static_addr = *(DWORD*)(static_addr + 0x255AF150);
static_addr = *(DWORD*)(static_addr);
static_addr = *(DWORD*)(static_addr + 0xD48);
static_addr = *(DWORD*)(static_addr + 0x0C);
static_addr = (DWORD*)(static_addr + 0xC4);
static_addr = *(DWORD*)(*static_addr + 0x08);
static_addr = *(DWORD*)(static_addr + 0x44);
static_addr = *(DWORD*)(static_addr + 0x10);
drifter_hp = (double*)(DWORD*)*(DWORD*)(static_addr + 0x1FD8);
drifter_ammo = (double*)(DWORD*)*(DWORD*)(static_addr + 0x268C);
 

Написание трейнера

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

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

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    5 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии