Перейти к содержанию
  • записей
    46
  • комментария
    23
  • просмотра
    3 292

О блоге

 

 

Записи в этом блоге

 

[Alpha protocol] Управление скоростью показа диалогов. Часть 2. Исследование+

Статьи придется растянуть ещё на 1-2 т.к. я свалился с температурой. Но сегодня не об этом, для начала нам необходимо вспомнить структуру построения диалогов.   [B01_P_INTRO_01_000 SoundNodeWave] SpokenText="Ты очнулся, очень хорошо - я не знала, сколько ты проваляешься. Эти транквилизаторы в конце концов перестали действовать." Comment="NOTES[], TRIGGER[], GLOSSARY[]" bMature=False Subtitles[0]=(Text="Ты очнулся, очень хорошо - я не знала, сколько ты проваляешься. Эти транквилизаторы в конце концов перестали действовать.") Необходимо обратить внимание на идентификатор, а точнее на SoundNodeWave, в прошлой статье я забыл указать, что это класс UE3, по сути это звуковой узел, работающий с локализацией, который содержит данные об воспроизводимом файле, а также его характеристиках.   Но для нашей цели, а именно увеличения времени показа субтитров, надо обратить внимание на массив Subtitles, а дальше просто, посмотреть в документацию.     смотрим на структуру array<SubtitleCue>     Линия текста субтитров и время, которое субтитры должны отображаться. Из примера выше:   Text="Ты очнулся, очень хорошо - я не знала, сколько ты проваляешься. Эти транквилизаторы в конце концов перестали действовать."   Вот мы и нашли способ для увеличения времени показа субтитров, имея на руках только гугл и файлы локализации. Да, он относительно долгий (нам надо будет переписать все субтитры, а их там 598 файлов). Можно конечно скрипт написать, который подправит все, но должен же быть способ проще. Но насколько я понял по локализации, то проблема в быстрых субтитрах именно в ней, в нормальной английской версии, субтитры разделены на группы, а в русской все в одной строке.   Eng: [B01_P_INTRO_01_000 SoundNodeWave] SpokenText="Good, you're awake - I wasn't sure how long you'd be under. Those tranquilizers wore off fast." Subtitles[0]=(Text="Good, you're awake - ") Subtitles[1]=(Text="I wasn't sure how long you'd be under. ") Subtitles[2]=(Text="Those tranquilizers wore off fast.") bManualWordWrap=True Comment="NOTES[], TRIGGER[], GLOSSARY[]" bMature=False Rus: [B01_P_INTRO_01_000 SoundNodeWave] SpokenText="Ты очнулся, очень хорошо - я не знала, сколько ты проваляешься. Эти транквилизаторы в конце концов перестали действовать." Comment="NOTES[], TRIGGER[], GLOSSARY[]" bMature=False Subtitles[0]=(Text="Ты очнулся, очень хорошо - я не знала, сколько ты проваляешься. Эти транквилизаторы в конце концов перестали действовать.") В следующей статье, мы будет находить объект SoundNodeWave, реверсить его структуру, восстанавливать в ReClass и писать скрипт в CE, который будет подсовывать в массив SubtitleCue указанное нами время. Или оставить это моим читателям как домашнее задание?

partoftheworlD

partoftheworlD

 

[Alpha protocol] Управление скоростью показа диалогов. Часть 1. Исследование

В игру я не играл, поэтому начнем с того что известно, используется движок в игре Unreal Engine 3. Это на самом деле круто, у движков UE достаточно упорядоченная структура данных, что во многом облегчит исследование и модификацию игры. Покопался в папках игры и нашел файлы с субтитрами, которые привязаны к звуковому диалогу, о чем и была речь в прошлой статье.  Субтитры выглядят вот так:   [E08_D_LELAND_01_003 SoundNodeWave] // идентефикатор воспроизведенного звукового диалога, указывающий миссию, имя персонажа и номер диалога SpokenText="If by home, you mean prison. Mr. Westridge first spoke to you in this room, didn't he?" // Озвученный текст Subtitles[0]=(Text="If by home, you mean prison. ") // Subtitles[1]=(Text="Mr. Westridge first spoke to you in this room, didn't he?") // Субтитры bManualWordWrap=True // Возможно, пропуск диалогов по нажатию кнопки Comment="NOTES[], TRIGGER[], GLOSSARY[]" // ??? (Возможно, добавление в записи, типа логирование диалогов) bMature=False // Цензура     Хех, за 10 минут мы добыли всю нужную информацию для реверса. Пока порядок действий таков:   Найти название идентификатора диалога, поставить на него бряк. Найти функцию, которая читает из файла с диалогами идентификатор, чтобы получить доступ к функции, которая будет считывать диалоги, а после рисует их. А также, найти прямую связь между запуском озвучки с субтитрами.     Продолжение следует...

partoftheworlD

partoftheworlD

 

Разбор работы системы диалогов в играх

Итак, это будет предисловие к основной статье, здесь мы рассмотрим структуру диалогов и как они создаются. Во вторник я освобожусь и уже займусь написанием основной статьи.   Существует всего несколько типов привязки диалогов для показа игроку: К какой-либо ситуации(триггеру) К нажатию клавиш К среднему времени чтения диалога. К озвучке   Чаще всего используется 4 вариант, но есть извращенцы, которые любят смешивать 1-2, 1-3.   Первая привязка это привязка к триггеру, когда игрок подходит к определенному нпц с каким-либо квестовым предметом, то срабатывает триггер и запускается диалог.   Вторая привязка это привязка к нажатию клавиш, т.е подойдя к нпц диалог не будет запущен, пока не будет нажата клавиша.   Третья привязка по времени, обычно в таком варианте используется константа для показа диалогов, значение константы рассчитывается командой специалистов по *заумное название исследований поведения и информации тестировщиков*   Четвертая привязка, субтитры привязываются к озвучке т.е если началась озвучка, то после начинаются рисоваться субтитры, а как только заканчивается воспроизведение файла с диалогом, то и субтитры пропадают.   В играх на UE, Unity диалоги делаются с помощью чертежей, либо написанием кода.

  В других играх через скрипты.   Но что объединяет эти способы? Инициализация пользовательского интерфеса. Парсинг игровых файлов, для загрузки диалогов в память.   Отлично, теперь мы знаем, что нам необходимо искать, либо находить функцию рисования субтитров, через поиск текста в памяти, либо будем искать обработчик/проверку, которая будет проверять запущена ли озвучка, и когда она заканчивается. Скорее всего, используется QueryPerformanceCounter для вычисления момента когда остановится озвучка и выглядит как-то так. rax, rbx, rcx = 0 rax = QueryPerformanceCounter() rbx = GetDialogTime("dialog_+" i "+.wav") rcx = rbx + rax while(bDialogEnabled): if rax > rcx: clear_text(); Dialogs[i]++; else: draw_text(Dialogs[i]) Вдруг это предисловие сможет указать верный путь для тех, кто не хочет ждать когда за него все сделают. Если такие найдутся, то пишите о результатах в комментариях к этой теме(пока что, позже все комментарии будут перенесены в основную статью)

partoftheworlD

partoftheworlD

 

[Dishonored] История о там как решал проблему с русской версией игры

Добро пожаловать на борт, мои юные любители моря и реверса. На этот раз мы решим проблему игры, разработчики которой не стали этого делать уже на протяжении 6 лет.   На весь реверс ушло порядка 50 рюмок рома, 6 подбитых кораблей британской империи и конечно же несколько портовых шл..., ах да, реверс. С языковыми winapi я не работал до этого момента и поэтому пришлось вручную дебажить весь код от инициализации игры, до создания интерфейсов и всякой не интересной ерунды.   Итак, изначально у нас было решение проблемы в виде костылей, что для фикса надо сменить язык в системе или текущую клавиатуру на английский, но кому это нравится? Можете спросить: «Зачем отлаживать код вручную, когда есть трассировка?»
Ну что-ж, мои маленькие пираты ответ прост, игра при использовании трассировки или отладки с обходом вызывает странный код и пропускает вообще все, что нам нужно исследовать, который при отладке с заходом не вызывается.     Всего пришлось дебажить около 10к инструкций, чтобы найти винапи которая выгружает, угадайте что? Правильно, идентификатор локали ввода. После вызова этой функции, у нас и пропадает из трея языковая панель и ломается локаль. Итак, мы нашли виновника, и что нам делать с ним? Нет, мы не будем скидывать разработчиков за борт. (а стоило бы)     Теперь нам нужно понять, как это все работает и откуда берется этот идентификатор локали ввода.   Отправляемся в бухту знаний и таинств…     Гуглим, гуглим, гуглим и наконец нагуглили winapi с именем LoadKeyboarLayout, круто,  отправляемся снова в дом к прекрасной иде, которую за все время нашего путешествия встречаем впервые, переходим на адрес инструкции с функцией выгрузки идентификатора. Вот и весь путь идентификатора.       Нашли мы LoadKeyboarLayout и теперь нас будет интересовать только первый аргумент этой функции, почему так? Да просто потому что мы умеем читать документацию и знаем, что значение первого аргумента равно 409, будет работать как положено, только если дефолтный язык в системе будет английский, для русского языка, необходимо использовать значение 419 это можно найти в мсдн.     В чем же заключалась проблема? Я думаю в лени разработчиков, а если судить о коде, проблему вызывала загрузка английской локали, но если её нет, то и загружать нечего, а значит и выгружать, а значит будут проблемы. Фикс заключается в исправлении значений загружаемой локали для распакованной версии, либо создании загрузчика для стимовской версии, который бы фиксил все это каждый запуск.   Всех с днем пиратства!   P.S Ах, да совсем забыл написать, все эксперименты проводились на распакованной версии игры, без Steam DRM т.к. со Steam DRM весь файл зашифрован и найти что-либо или пропатчить невозможно. Этим утром нашел ещё проблему некорректной инициализации steamapi, из-за чего игра время от времени крашилась. Сам по себе Steam-DRM инициализирует steamapi, но раз мы его вырезали, то естественно будут проблемы при обращении к нему. Для того чтобы решить проблему со steamapi, нам необходимо создать текстовый файл около файла steam_api.dll с именем steam_appid.txt и указать в нем ID игры для принудительно инициализации.    

partoftheworlD

partoftheworlD

 

[Counter-Strike Global Offensive]RCS аналог no recoil

Сегодня сделаем Recoil Control System, а точнее его простейшую реализацию. Используя такой способ можно добиться стрельбы в одну точку на любом расстоянии с учетом значений разброса и ускорения отдачи. Все как обычно, открываем иду и ищем смещения.  m_Local m_aimPunch   Ещё необходимо найти адрес управляющий координатами камеры, но это слишком просто чтобы останавливаться на этом. Итак, теперь надо компенсировать отдачу, для этого мы просто складывает старый вектор Punch с новым, можно умножать на 2, но точность будет отличаться на 10-15%.   DWORD m_dwLocal = 0x0; DWORD m_vecAimPunch = 0x0; vec3 m_vecOldPunch = {0,0,0}; auto CLocalPlayer = m->read<DWORD>(dwClient + 0x00C5C86C); auto CClientState = m->read<DWORD>(dwEngine + 0x586A74); vec3 m_vecAngles = m->read<vec3>(CClientState + 0x4D10); vec3 m_vecPunch = m->read<vec3>(CLocalPlayer + m_dwLocal + m_vecAimPunch) static vec3 *tempAngles = new vec3(m_vecAngles); tempAngles->x -= m_vecPunch.x + m_vecOldPunch.x; tempAngles->y -= m_vecPunch.y + m_vecOldPunch.y; tempAngles->z = 0; m->write<vec3>(dwvecAngles + 0x4D10, tempAngles); m_vecOldPunch = m_vecPunch; delete tempAngles; Это конечно не так просто как в Warface, но затруднений вызвать не должно.      

partoftheworlD

partoftheworlD

 

[IDA Pro] Отслеживание интересующего регистра по функции

Сегодня напишем простейшую реализацию функции поиска используемого регистра в коде при статическом анализе, как когда-то показывал этот функционал в radare2. Немного модифицированный скрипт стоит у меня на вооружении для динамического анализа т.е. определяет где впервые адрес появляется, как рассчитывается, дампит смещения и пробует найти сигнатуру на статический адрес в коде. В общем немного подумав и почитав доки по иде, тоже сможете такое сделать без особых проблем.   Скрипт пишется минут за 15-20, но в дальнейшем экономит нереально много времени.   Единственная проблема возникла с получением точно адреса инструкции, во избежании повторений в выводе и была написана альтернатива функции range.  # подключаем необходимые библиотеки import idc # стандартные модуля для общения между идой import idaapi import re # для расширения функционала поиска reg_value = r'.*\[r14\+.*' # то, что будем искать def range_reverse(start, stop): # аналог функции range for i in FuncItems(start): if i < start and i >= stop: yield i ea = ScreenEA() # получение текущего адреса курсора current_function = idaapi.get_func(ea) # получение информации о функции # перебор инструкций по адресам for inst_addr in range_reverse(ea, current_function.startEA): disasm_output = idc.GetDisasm(inst_addr) # вывод дизассемблированной инструкции по её адресу if re.findall(reg_value, disasm_output): # фильтрация по нужному тексту print("0x%X" % inst_addr, disasm_output) Раз скрипт уже написали(скопипастили), приступим к тестам.   И так, у нас есть инструкций работающая со скорострельностью, нам надо посмотреть всякие интересности, типа проверок, что ещё работает с базовым адресом таймера скорострельности и т.д.     Выполняем скрипт и получаем всю информацию о интересующем регистре, так же, чтобы избавится от инструкций чтения и выводить только инструкции записи, можно добавив одну проверку, но с этим вы сами сможете справиться, я надеюсь вы меня не расстроите.     Так же можем отследить как вычисляется база следуя за регистром, переходя двойным кликом по адресу в окне вывода.     Это ведь проще, чем руками скролить функцию под 10-20к инструкций. В общем, если лень, что-то делать — автоматизируй или не делай.  

partoftheworlD

partoftheworlD

 

[IDA Pro] Экспорт информации из старой базы в новую

Тыкался тут на днях в кнопочках у иды и как оказалось у неё есть экспорт, и давно был, просто находятся в самом неожиданном месте. Так что, я был удивлен и расстроен одновременно. А расстроен тем, что я писал плагин для экспорта, руководствуясь словами "раз нет какого-то функционала, то сделай".   Итак, чтобы задампить всю информацию из старой базы надо будет кликнуть на File->Produce file -> Dump database to IDC script.   В итоге ида создаст для нас скрипт со всей информацией, останется лишь после анализа нового файла запустить его и база восстановится. Пример скрипта.     Если вы анализировали дамп, то советую перебазировать его по первому сегменту к 0 старую и новую базу, перед созданием скрипта для экспорта. И восстановление занимает достаточное количество времени(возможно, зависит от того на сколько восстановлена база) поэтому это отличная возможность, чтобы сходить за чаем.

partoftheworlD

partoftheworlD

 

[Grand Theft Auto Online] Поиск скрытых структур за счет игровых скриптов

Необходимые инструменты чтобы начать искать и делать скрытые структуры: OpenIV, Cheat Engine, GTA HLD, какой-нибудь ЯП для автоматизации действий и конечно же GTA. По сути этот метод позволяет использовать нативные вызовы без инъекции/скриптов в игру и делать с ней все что вздумается, спавнить оружие, спавнить машины, накручивать деньги, перемещать других игроков и т.д возможности безграничны. Итак, открываем OpenIV, находим скрипт freemode, экспортируем и декомпилируем с помощью GTA HLD , на выходе получается такой псевдокод.     Нас интересуют только глобальные переменные - Global_XXXX, где XXXX это значение индекса по которому будем вычислять положение скрытой структуры, кстати все это делается в той же функции которая была в прошлой статье про гта и чтобы не создавать костыли, достаточно зареверсить функцию и скопировать из неё код.   Вычисления будут выглядеть примерно так, newGlobalPageBase = struct_array + 8 * (index >> 18 & 0x3F) , таким образом мы получаем страницы памяти по 2Мб в которых будут скрытые структуры, а чтобы получить конкретный адрес, для работы с глобальной переменной,  value = newGlobalPageBase + 8 * (index & 0x3FFFF) Рассмотрим все это на конкретном примере, например на таймере одной из заданий.     Теперь, нам необходимо посчитать, либо индекс до начала структуры 0x40001, либо сразу до значения просто сложив 0x40001+0x454B. Подставляем, индекс в наш код и хоба     У нас есть адрес таймера игрового скрипта, который бы мы не смогли найти вручную с помощью поисковика, просто  потому что это статическое значение. Этот способ проще, быстрее и дает нам безграничные возможности по взлому игры просто тыкаясь в файлы скриптов. Именно таким образом и работает накрутка денег, спавн машин в онлайне, только манипулируя памятью игры.   Благодарности:  Reversal Thread 7h3 7r14ngl3

partoftheworlD

partoftheworlD

 

Основа для нейронной сети основанная на OpenCV

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

partoftheworlD

partoftheworlD

 

Скин для Vivaldi

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

partoftheworlD

partoftheworlD

 

[Grand Theft Auto Online] Stealth money или поиск скрытых структур для получения денег

Необходимые инструменты чтобы начать искать и делать стелс деньги: OpenIV, IDA, Cheat Engine и конечно же GTA с доступом к онлайну. Так же, если вы все собираетесь проделывать на основном аккаунте, то советую после всех манипуляций удалить персонажа и создать нового, так очищается список транзакций(но это не точно). Для начала нам необходимо зайти в одиночный режим, чтобы найти инструкции работающие с деньгами.     Это нам необходимо для того, чтобы не искать деньги в онлайне, в любом случае там найти мы их не сможем. Дальше, переходим в онлайн и так же переходим в дизассемблер. Нам необходимо узнать как рассчитывается rdx.     Ставим бряк на инструкцию mov rdx, [r8+rax*8] и в регистре rdx видим 00007FF739F26820, перейдем на этот адрес в IDA. Да это же массив с указателями на скрытые структуры, нам необходимо найти единственную работающую с деньгами, я думаю вы сами справитесь с этим, поэтому, как только мы нашли структуру связанную с деньгами высчитаем её индекс, он нам пригодится во время эмуляции транзакции.   (00007FF739F268A0 - 00007FF739F26820) / 8 = 0x10  
Теперь, нам придется восстановить смещения, проблема в том, что эта структура работает только с миссиями, ну что ж пойдем работать, но сначала необходимо найти начало структуры внутри указателя на структуру денег, для этого ещё немного по реверсим, зная адрес начала структуры поставим на инструкцию которую нашли до этого бряк с условием.     И идем выполнять миссию, главное получить деньги, поэтому советую выбрать одиночную миссию т.к. во время бряка с условием у вас будет фпс около 3-4 кадров(на моем компуктере миссия в 5 минут растянулась на 2 часа и пришлось перепроходить т.к. бряк не сработал, ага победитель по жизни.). Так что вас ожидает боль и страдания, но как только найдем начало нашей структуры, выставляем сравнение структур с блокировкой текущего состояния структуры и снова идем проходить миссию(в этот раз все будет на много лучше, обещаю)

  После прохождения миссии, снова смотрим в эту структуру, получаем вот такие значения, это и есть смещения работающие с транзакцией.   #1 000000000000004A * 8 = 1 (offset 0x250 ) //активатор транзакциии #2 0000000000000000 * 8 = 5 (offset 0x00 ) //5 счет банка, 4 наличка #3 0000000000000001 * 8 = 3587 (offset 0x08 ) //сумма #4 0000000000000002 * 8 = 1 (offset 0x10 ) //активатор #5 0000000000000003 * 8 = BC537E0D (offset 0x18 ) //хеш #6 0000000000000004 * 8 = 00000000 (offset 0x20 ) //сервисный хеш #7 0000000000000006 * 8 = 57DE404E (offset 0x30 ) //хеш #8 0000000000000007 * 8 = 00000000 (offset 0x38 ) //хеш типа действий #9 0000000000000047 * 8 = 4 (offset 0x238 ) //?? #10 0000000000000048 * 8 = 7 (offset 0x240 ) //?? #11 000000000000000A * 8 = -1 (offset 0x50 ) //?? #12 0000000000000028 * 8 = 0 (offset 0x140 ) //?? #13 0000000000000049 * 8 = 0 (offset 0x248 ) //?? #14 000000000000004B * 8 = 0 (offset 0x258 ) //?? #15 000000000000004F * 8 = 0 (offset 0x278 ) //?? Теперь нам необходимо найти новые хеши вместо нулей, для этого запускаем OpenIV и ищем файл с названием networkshop, и подставляем все хеши из блоков actiontype и services по нашим смещениям, actiontype задает тип транзакции т.е. добавить, отнять деньги и т.д, services задает от кого мы получили деньги, от R*, с гонок, со ставок и т.д.     Комбинаций много, но что делать, халява это дело времязатратное, тем самым перебирая хеши выстраиваем порядок выполнения транзакции, но иногда хеши фиксят, чтобы не допустить распространение читов.   BC537E0D
57DE404E
Эти 2 хеша предустановлены их можно найти в коде тем самым определив и функции работающие с транзакциями. Самое время начать писать код и тестировать. Хеши не рабочие в этом примере кода, чтобы любителям ничего не делать, пришлось пошевелиться.😉 //активация транзакции DWORD64 activate = 0x1; //установка хешей валидации транзакции WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x60), &activate, sizeof(activate), 0); DWORD64 value = 0xBC537E0D; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x68), &value, sizeof(value), 0); //установка суммы транзакции int amount = 10000000; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x58), &amount, sizeof(amount), 0); //установка хешей действий и откого будем получать деньги value = 0x762D6BF6; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x70), &value, sizeof(value), 0); value = 0x3B6B7024; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x88), &value, sizeof(value), 0); value = 0x57DE404E; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x80), &value, sizeof(value), 0); //установка для того чтобы транзакция не зависла в ходе выполнения по разным причинам value = 0x7FFFFFFF; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x50), &value, sizeof(value), 0); value = 0x07; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x290), &value, sizeof(value), 0); //куда будем давать деньги 4 наличка, 5 счет банка value = 0x04; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x288), &value, sizeof(value), 0); value = 0x01; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x2a0), &value, sizeof(value), 0); //деактивация транзакции value = 0; WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x78), &value, sizeof(value), 0); WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x298), &value, sizeof(value), 0); WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x2a8), &value, sizeof(value), 0); WriteProcessMemory(hProcess, reinterpret_cast<PBYTE*>(buf + 0x2c8), &value, sizeof(value), 0); Инструкции работающие со значениями в функции.     Ну и результаты, так же я испытываю новый метод поиска подобного функционала, если все пройдет удачно, то скоро будет новая статья, в разы проще этой и быстрее. Ну, что вы молодцы, если дочитали до сюда, можете взять печеньку.      

partoftheworlD

partoftheworlD

 

Скрытые структуры, что это такое и с чем их едят.

Сегодня у нас будет прелюдия перед основной статьей, так что желательно быть готовым ко всему. Поехали. Скрытые структуры, что они из себя представляют? Скрытая структура это какая-то структура находящаяся в любой части используемой кодом структурой(назовем её мега-структурой), обычно мега-структуры огромных размеров(не зря же мы её так назвали) и содержат в себе по 10-100 скрытых структур, в ГТА например она занимает около 105кб. Как вообще игра находит скрытые структуры? Да, как и везде адрес мега-структуры + index * 0x8 для 64 битных приложений. А теперь поговорим о том как они работают, по сути скрытые структуры просто принимают в себя значения, а в коде это выглядит как обычная функция примерно так.

  Если по простому, то скрытые структуры можно представить как обработчики значений, которые работают постоянно, но как только определенное смещение будет равно определенному значению, то что-то случится. Одна из проблем при использовании таких структур им необходимо подавать все переменные в определенном порядке, например, если мы не активируем транзакцию, то и значение передаваемое не будет обработано, оно будет просто пропущено. Находить такие структуры обычным поиском практически не реально, значения попадающие в неё перезаписываются нулями через 10-20 мс в среднем. Поэтому нам и необходим реверс-инжиниринг. Еще одна проблема конкретно для гта, это расширение функционала структуры, а именно получение рабочих хешей, скажем так, хеши нужны для того чтобы транзакция считалась валидной для определенного количества денег, например для значений денег от 100к до 1 миллиона будет один хеш, для того чтобы получить от 1 миллиона до 10 миллионов нужен другой, но об этом расскажу в следующей статье. Думаю, пока информации хватит, если возникнут вопросы по таким структурам, то пишите в комментариях.    

partoftheworlD

partoftheworlD

 

[RTTI] Автоматизация дампа структур класса

Хехей для начала мне лень писать статью с картинками и прочим, кто поймет, тот молодец, а кто нет, значит не особо нужно. Ну в общем я когда-то показывал что можно сделать с динамической типизацией, а точнее с классами, так что мы будем использовать код сканера сигнатур, немного переписав его чтобы сканировать всю память компьютера, чтобы дампить все предметы которые используют выбранный нами класс. Для начала подготовим базу, берем свой сканер сигнатур, либо из паблика. 
Теперь нам необходимо изменить  код который указывает диапазон по которому мы будем брать страницы виртуальной памяти.
Он выглядит примерно так: while(offset < size) { VirtualQueryEx ... Для того, чтобы получить минимальный и максимальный адрес памяти будем использовать GetSystemInfo.
  SYSTEM_INFO si; GetSystemInfo(&si); auto seg_base = si.lpMinimumApplicationAddress; while (seg_base <= si.lpMaximumApplicationAddress) { if (VirtualQueryEx(hProcess, reinterpret_cast<DWORD64*>(seg_base), &mbi, sizeof(MEMORY_BASIC_INFORMATION))) ... Необходимо отфильтровать страницы, чтобы уменьшить время поиска значений и работы программы, для этого добавляем такую проверку.   if ((mbi.State == MEM_COMMIT) && (!(mbi.Protect & PAGE_GUARD)) || (!(mbi.Protect & PAGE_NOACCESS))) Теперь в цикле будем считывать адреса с шагом в 4 байта каждой оставшейся страницы памяти.
  for (auto i = 0; i < mbi.RegionSize; i+=4) { ... ReadProcessMemory(hProcess, reinterpret_cast<PBYTE*>(base + i), &wrBuff, sizeof(wrBuff), nullptr); Настало время для проверки значений, это можно сделать с помощью memcmp и загрузить результат в контейнер для удобного хранения. Выглядит это примерно так.     Из минусов такой способ относительно долгий, повысить скорость работы можно за счет многопоточности.
А из плюсов это дает нам возможность забить на поиски структур хранящих список вещей, списки оружия или же игроков. Т.е. нам достаточно сгенерировать сигнатуру указывающую на нужный нам класс, чтобы находить адреса всех структур, которые используют выбранный нами класс. Сделать ESP, Radar Hack или просто взломать список вещей в инвентаре не прикладывая усилий, вот что я люблю.

partoftheworlD

partoftheworlD

 

[Grand theft Auto Online v1.43] Stealth Money (20 миллионов в секунду) Update 2

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

partoftheworlD

partoftheworlD

 

[JS] Старая творческая студия для youtube

Начал учить JS еще и из-за того, что меня ютуб начал раздражать перекидывая постоянно на новый дизайн творческой студии, даже если переходить на классический и писать матерные слова в окне фидбека. Так, что если я не один такой, то вот скрипт для Tampermonkey и его альтернатив под разные браузеры. Скрип во время загрузки страницы подменяет новый адрес кнопки ведущий на обновленный дизайн, на старый.   // ==UserScript== // @name Old youtube video manager // @version 0.1 // @author partoftheworlD // @include https://www.youtube.com/* // ==/UserScript== (function() { function replace_C() { var vm_button = document.querySelector("a.yt-uix-button.yt-masthead-picker-button.yt-masthead-picker-button-primary.yt-uix-sessionlink.yt-uix-button-default.yt-uix-button-size-default"); if(vm_button && vm_button.href.includes("dashboard") || vm_button.href.includes("studio")) { vm_button.href = "/my_videos?ar=1&o=U"; vm_button.text = "Творческая студия"; } } document.addEventListener('DOMContentLoaded', replace_C()); })();  

partoftheworlD

partoftheworlD

[IDA7] Function String Associate скрипт Update 0.05

Накидал небольшой скрипт, который выполняет тоже самое что и плагин FunctionStringAssociate, просто потому что, для новой версии плагин часто вылетает, нет кроссплатформенности, его нет под x86, для того чтобы был необходимо качать несколько библиотек, какие-то файлы и настраивать VS, никаких альтернатив не нашел, а также скрипт наверняка будет работать после следующих обновлений, даже если вдруг Hex-Rays снова решат обновить API. Сейчас это просто скрипт, в дальнейшем прилеплю меню и доступ из вкладки плагинов и увеличу скорость работы (как разберусь с новомодными модулями, которые нагуглю). Если кто-то будет пользоваться скриптом, то пожалуйста пишите, если возникнут какие-то проблем, а баги сразу на гите оформляйте.   Для чего нужен?     Плагин пригодится для беглого анализа всех функций в виртуальной таблице, при рассматривании скриптовых движков, может помочь найти всякие функции, относящиеся к обработке скриптов скриптовым движком игры, ну и конечно же помогает восстановить виртуальные таблицы из библиотеки игры с подписанными функциями MacOS в библиотеке Windows.   Как пользоваться? Кидаем в иду нужный нам файл, так же кидаем файл с плагином в папку plugins Заходим в пункт Edit/Plugins будет надпись FunctionStringassociate тыкаем на неё. Как только запустится скрипт в окне Output window будет надпись Launching По завершению анализа в том же окне получаем информацию о том сколько было просканировано функций и добавлено комментариев.     Возможно работоспособность может зависеть от степени анализа файла т.е. некая зависимость от количества анализированных функций относительно всех возможных Вроде бы все проверил, но так как скрипт писался всю ночь и день тестировался, то могут быть баги, которые я упустил.   Demo:     https://github.com/partoftheworlD/IDA7py_FunctionStringAssociate/     Обновления    

partoftheworlD

partoftheworlD

[Hotline Miami] Бессмертие за счет отключения коллизии

Даже удивительно почему никто не сделал бессмертие для этой игры, ведь все достаточно просто. Раз у нас нет индикатора здоровья, значит необходимо найти триггер, который бы говорил "вот тебе дали по лицу и ты умер", а значит нам нужны *эффекты смерти и грустная музыка*. Если подумать из всего, от чего мы можем оттолкнуться стремясь к бессмертию, использовать сможет только координаты. Итак, план действий: Находим координаты Находим участок кода отвечающий за коллизию Делаем бессмертие за счет отключения коллизии, фильтруя для чего нам нужна коллизия, а для чего нет или переходим к пункту 4. Делаем бессмертие за счет нахождения триггера, изменяя условный переход, который больше не будет выводить сообщение о смерти и грустную музыку.   Бессмертие за счет отключения коллизии это самый простейший способ, но он для любителей поглазеть в структуры игрока, npc, стен, в поисках их уникальных ID, чтобы написать хороший фильтр. Бессмертие за счет изменения триггера это немного дольше, плюс могут быть проблемы, если игра использует скрипты, то скорее всего после отключения триггера и когда нам дадут по лицу мы получим софтлок и тут уже спасет только перезапуск.   Находим координаты, ставим бряк на доступ, необходимо немного побегать, чтобы отделить инструкции для передвижения от инструкций работающими с расчетом коллизии.     До красной линии это все инструкции которые работают с персонажем при движении, зеленым помечены инструкции, появляющиеся после того как нас ударили. Как только переходим по самой первой инструкции после удара, видим такой интересный код.     В общем здесь и происходит вся магия, загружаются значения, загружается коллизия предметов и npc, так же значения коллизии к пример стен статичны 1.875, немного пореверсив, можем написать скрипт, но чтобы он работал как бессмертие, его необходимо будет дописать с учетом состояния врага или же дистанции от врага, чтобы коллизия отключалась когда враг собирается нас атаковать. Это нужно для того чтобы игра не крашилась при переходе между локациями, т.к. при отключении коллизии игра не будет знать где можно спавнить игрока, а где нет.   А вот и функция расчета коллизии, входные данные для функции это размер коллизии предметов, текущие координаты npc/врага, а так же скорость:   Ну вот и все, небольшая демка и бонус будут чуть ниже, а так же скрины показывающие, что будет если изменять размер коллизий. По времени на взлом ушло около 20 минут, самое сложное было найти координаты. 😁 Если будет интерес, то напишу ещё одну статью в которой будем рассматривать бессмертие с помощью второго способа, а т.е. поиск триггера который бы говорил "вот тебе дали по лицу и ты умер", а значит нам нужны *эффекты смерти и грустная музыка*   Демо 1:     Демо 2:     Размер коллизий:          

partoftheworlD

partoftheworlD

[Radare 2] Статическая "отладка" кода

Всем радар, пацаны. Сегодня расскажу про эмуляцию кода или ESIL, это очень крутая штука позволяющая "отлаживать" код без отладки, но для начала небольшой экскурс.   Вы можете спросить, а какова цель ESIL, да и кому он нужен, когда есть отладчики? Ну, во-первых, не всегда нужный код срабатывает, когда нам это нужно, а ждать минуты, часы, дни, пока он сработает тоже не вариант. Во-вторых, при анализе малвари даже под виртуалкой опасно запускать исследуемую программу т.к. она может нанести вред хосту. В-третьих, иногда нет физического устройства со специфичной архитектурой.   Для того чтобы решить эти проблемы и придумали эмуляцию. Radare2 поддерживает для эмуляции множество архитектур, но не полный список инструкций, для некоторых из них (но это решается со временем), а только основные для облегчения нам с вами жизни, ну и конечно ESIL можно использовать в скриптах. Список поддерживаемых архитектур на 2015 год:   Итак, начнем. Для начала посмотрим на все команды для работы с ESIL.     Нам понадобятся только несколько из них. Для инициализации виртуальной машины используется команда:   Теперь необходимо установить начало, откуда начнем эмулировать код т.е. установить адрес в регистр EIP. Для примера накидал небольшую программку, которая что-то считает.   Выберем 0x1400010c7, как раз до этой инструкции загружается значение из буфера в eax.   Таким же образом загрузим в rax какое-нибудь значение, например 0x1234   И теперь необходимо установить триггер, чтобы эмуляция выполнялась только до этого адреса или условия. Триггер является неким подобием бряка во время отладки. Для триггера по адресу используем   теперь, как только RIP станет равным нашему адресу, то эмуляция остановится.   Для триггера с условием будем использовать другую команду к примеру, нам надо чтобы триггер сработал, когда eax станет больше 0x1234.   Конструкция выражений не самая удобная, но понятная, к примеру инструкция sub rsp, 8; будет выглядеть как "8, rsp, -"  т.е даже не зная опкодов каждый сможет понять, что делает инструкция на любой архитектуре.   Для наглядности посмотрим состояния регистров до эмуляции и после. До:   (Забыл сделать скрин по xmm регистрам, но там были одни нули)   После:   Так же можно эмулировать код пошагово с помощью команд   Ну думаю на этом все, надо будет испробовать такой способ в следующем видео для статического расчета указателей на разные статы игрока, типа здоровья или еще чего от базового адреса.

partoftheworlD

partoftheworlD

 

[Ubisoft The Crew] Как я поборол ошибку #0_35 и смог все же поиграть

Это больше юмористический пост нежели технический, просто забавная история про китайский модем и юбисофт. Не так давно проходила распродажа, по которой я урвал The Crew, в тот же день установил её и думал погонять от души. Но не тут-то было, при входе стал получать ошибку, что сервера Ubisoft не доступны, это обычное дело для Ubisoft, зашел на форум юбиков, смотрю надпись: "Сервера юбисофт для некоторых игроков не доступны". Спустя неделю ситуация не изменилась, погуглив оказалось, что игра не умеет открывать порты и из-за этого может быть ошибка. Отключив роутер и запустив интернет прямиком по кабелю, игра запустилась, дело было в роутере. Что-ж раз так, начал открывать порты, к моему удивлению открылись все порты кроме нужного, т.е. из 65535 портов не открылся только 1 проверял с помощью nmap. Нашел довольно странный способ, но рабочий, открывать 3000 порт с помощью торрент-клиента установив в нем кастомный порт. Для того чтобы избавится или хотя бы уменьшить костыли набросал простой клиент, который бы слушал нужный мне порт на python, как итог все прекрасно работало, но, чтобы избавится от зависимости устанавливать python пришлось переписать на cpp. В итоге перед запуском клиента надо запускать этот костыль, чтобы поиграть, а все из-за лени юбиков. Вот и конец этой замечательной истории, когда ты платишь деньги за игру, так ещё должен фиксить проблемы игры всякими костылями, но причину почему не открывается 3000 порт на роутере так и не нашел, возможно что-то прописано в прошивке.   Не думаю, что кому-то нужен такой исходник, но все же приложу.    

partoftheworlD

partoftheworlD

 

[Sniper Elite 4] Практическая часть: реверс и понимание логики игры.

Ну что, хватит скучной теории, пора что-нибудь поломать. Хочу сказать сразу, к черту поиск значений, это скучно, не интересно, нудно и вообще поиск для слабаков. Первое, что необходимо это изучить игру, данная игра использует скриптовый движок, поэтому это для нас облегчит поиск необходимого участка кода.Раз движок использует скрипты, то есть высокая вероятность того, что некоторые данные будут как простые строки, ищем все строки и фильтруем по строке"Glow".     Можем выбрать любую строку и поставить бряк на доступ в памяти. Я выбрал "Dg_fOuterGlowMin", просто потому что немного ниже, есть строка указывающая тип этой текстовой переменной. И как только срабатывает бряк, у нас появляется такая функция, которая использует нашу строку.     После недолгих экспериментов и метода тыка, узнаем что эта функция проверяет видимость npc, т.е изменив значение возвращаемого значения мы можем использовать бинокль везде и всюду, стоя упершись биноклем в стену. Теперь, зная что делает эта функция изучим по подробней, что тут вообще происходит. Для начала, посмотрим, что, где, как, и почему здесь проверяют. Нас будет интересовать только несколько инструкций.   Выходим из функции и смотрим, что происходить выше по коду, а здесь у нас происходит вычисление id модельки, по id npc и получение её базового адреса, базовый адрес скорее всего является вложенной структурой используемой чисто для подсветки. А массив r15 хранит в себе все предметы, думаю это очевидный вывод, раз используются такие огромные индексы. | mov eax, dword ptr [rsp+rbx*4+0x50] | rsp = 5; rbx = 4; rbx является реальным id ... | lea rcx, qword ptr [rax+rax*2] | rax = 0x4B5; rcx = 0xE1F | add rcx, rcx | rcx = 0xE1F * 2; rcx = 0x1C3E | mov r9, qword ptr [r15+rcx*8+0x20] | получаем базовый адрес предметов; r15 массив с предметами + индекс * 8 + 0x20 Дальше, после получения базового адреса происходит передача его же первым аргументом, а дальше проверка видимости после которой получаем результат. И результат. Сама функция проверяет как я уже написал выше видимость, за счет дистанции между игроком и npс, так же проверяя, а не мешает что-либо игроку видеть npc.     Это только самая основа, радоваться ещё рано, ведь предстоит много работы.  Мы же не гуманитарии какие-нибудь, чтобы руками помечать цели.     Что ж, для одиночной игры этого могло бы и хватить, но для мультиплеера, придется ковырять ещё глубже, чтобы восстановить логику и порядок действий для воспроизведения этого в нашем будущем хаке. Да, да скорее всего для наглядности будем писать чит. Но это уже совсем другая история, ведь уже скачался сериал.
 

Виды внутриигрового wallhack

Данная статья будет выступать теорией перед практикой, для начала, если кто не знает что за "внутриигровой wallhack" это тот который сделал за нас разработчик. Выглядит это примерно так     А теперь о типах подсветки, в зависимости от движка, игра может подсвечивать все что угодно, либо какие-то конкретные предметы прописанные в недрах кода, подсвечивая контур вокруг модельки или же залив полностью. Но как игра это делает? От игры к игре делается это по разному, либо же ведется подсвечивание модельки постоянно, но при определенных обстоятельствах меняет цвет подсветки на зарегистрированный, т.е. игра постоянно всех подсвечивает, но если игрок "A" сделал действие "B", то игра меняет по смещению цвета подсветки значение к примеру на 0xEB и все, игрок "A" будет подсвечен, но если изменить значение по этому же смещение на 0xEC или 0xEA то ничего не произойдет. Это был первый популярный способ подсветки у игр, теперь перейдем ко второму. Этот способ обычно использует  вложенную структуру подсветки в структуре игрока т.е   pPlayer->pGlow->bActivator pPlayer->pGlow->fRed pPlayer->pGlow->fGreen pPlayer->pGlow->fBlue pPlayer->pGlow->fAlpha ... Все происходит достаточно просто, сначала для игрока "A" одна из функций игры активирует подсветку, а другая задает цвет в зависимости от ситуации, иногда они объединены в одну функцию. Привлекателен этот способ тем, что независимо что мы захотим подсвечивать, у всех структур (машин, npc, ящиков, птиц) по одному и тому же смещению будет указатель на подсветку.   А теперь поговорим о вариантах поиска и возможных проблемах.   Первая и основная проблема состоит в том, что большинство разработчиков делают подсветку объекта одноразовой из-за этого будет не реально выйти на это значение обычным поиском. В таком случае, я бы посоветовал найти структуру npc и с помощью поиска найти изменения во время подсветки, если все прошло удачно, то мы уберем все не нужное и зная примерные смещения можно сравнить структуры нескольких npc, чтобы найти единственно нужное смещение. Вторая проблема. Даже если найдем активатор подсветки, то надо хорошо разбираться в асме, просто потому, что в таких тонких делах декомпилятор может лишь запутать. Я бы посоветовал использовать radare2 для отслеживая инструкций т.к. там есть удобный фильтр для вывода только того дизассемблерного листинга, который нам нужен. Третья проблема. В некоторых играх для активации подсветки может использоваться не истинный id игрока, а id предмета на локации после загрузки всех предметов. К примеру, есть у нас структура npc у которого id будет равно 1, но, когда он заходит на локацию id модельки будет 150, т.е. id будет назначен после всех динамических моделек, которые уже загружены на локации типа шторок, столов, ящиков и прочего.   Отличия первого способа от второго в том, что в первом достаточно изменить цвет и все будет работать, во втором же нужно соблюдать определенный порядок, иначе ничего работать не будет, это не во всех играх, но такое практикуют.   Я ещё думаю на какой игре бы показать. но пока выбор останавливается на Sniper Elite 4.
 

Radare2 Скриптинг с python

Частенько в играх стали попадаться функции описывающие структуру какого-то класса, так вот в этих функциях есть смещения и описание, выглядит это обычно так.     Ужасно, не  правда ли? Так что сегодня пойдет речь о скриптах в radare2. Почему именно радар, а не ида, думаю ответ очевиден, он работает быстрее и позволяет анализировать только выбранные функции, а не весь файл. Итак, начнем писать скрипты, если вы  не знаете Python, то у radare2 есть поддержка многих языков для написания скриптов в этом можно убедиться посмотрев на табличку ниже. Но есть один модуль в python позволяющий сразу создавать таблицы для ReClass (https://github.com/70RMUND/ReClassMap)   pipe spawn async http tcp rap json plug lib buff C X X - X X X X X X X C++/Qt X X - - - - X - X - C# / F# X X X X - - - - X - D X - - - - - X - - - Erlang X X - - - - - - - - Go X X - - - - X - - - Haskell X X - X - - X - - - Java/Groovy - X - X - - - - X - Lisp - X - - - - X - - - NewLisp X X - X - - X - X - Nim - - - X - - X - X - NodeJS X X X X X - X X - X Ocaml - X - - - - X - - - Perl X X - X X - X - - - PHP - X - - - - - - - - Python X X X X X X X X X - Ruby X X - - - - X - - - Rust X X - - - - X - - - Swift X X X X - - X - X - Vala - X X - - - - - - - Clojure X X - - - - - - - - Более поддерживаемые языки NodeJS Python Swift C/Nim/Vala/C++   А теперь перейдем к написанию скрипта.       Вывод выглядит следующим образом [0xa4] ['"PartQuality"'] size 1 [0xa8] ['"PartLevelDelta"'] size 4 [0xac] ['"PartQualityDelta"'] size 4 [0xb0] ['"PartDestination"'] size 1 [0xb8] ['"RewardModelId"'] size 8 [0xc0] ['"CarChunkRequired"'] size 4 [0xc4] ['"CarChunkCount"'] size 4 [0xc8] ['"IsVehicleCompleted"'] size 1 [0xcc] ['"Score"'] size 4 [0xd0] ['"ActionPoints"'] size 4 Не идеально, но сойдет.
 

[C++ Reverse]И снова RTTI, но уже в деталях

Когда ещё начинал писать первую статью по использованию RTTI хотел приложить эту статью, которую когда-то увидел на black hat, но как обычно бывает потерял её с переездом на vivaldi, очень круто все расписано включая внутреннее устройство этих таблиц, что очень полезно для начинающего реверсера. bh-dc-07-Sabanal_Yason-WP.pdf   Ах, да, статья на английском.

[STALKER Clear Sky] Выход на структуру хранящую все предметы на локации

Вот и настал тот час, когда я со всем разобрался, на сегодня у нас рассказ о том, как легко и просто выйти на массив инвентарей на локации. Для начала нужно понять, что мы собираемся искать, найти структуру предмета не всегда легко, где-то объекты статичные, а где-то их можно катать по всей карте и искать координаты, но признайте это долго и глупо. Так что воспользуемся моим любимым RTTI. Если у вас IDA 6.8 вам понадобится плагин ClassInformer, если IDA 7, то в нем уже встроен парсер RTTI для vc и gcc.   Заходим в View -> Open subview -> Names нажимает CTRL+F и ищем к примеру, CWeaponAK74     Нам нужен самый первый адрес виртуальной таблицы, почему именно он, все просто, IDA сортирует виртуальные таблицы по размеру и почти всегда самый первый адрес(а значит самая толстая виртуальная таблица) будет тем чем нужно. Теперь ищем этот адрес в памяти игры.     Великолепно, нашли 6 адресов, это у нас будут производные от нашего класса CWeaponAK74, а если по-простому, то вокруг нас есть 6 автоматов. Теперь нужно найти какие инструкции используют эти автоматы, переходим в CE и ставим бряк на доступ в памяти на понравившемся адресе, я взял 5613D030. После срабатывания получаем много инструкций, но нам нужна только первая. Если возникнет вопрос почему именно первая, то все из-за типа бряка, раз бряк на доступ значит первая инструкция будет показывать место откуда вообще берется структура.     Перейдем в дизассемблер и сразу бросается в глаза эта инструкция, выглядит как какой-то массив, перебирающий по индексу что-то, значит надо это проверить. Ставим трассировку на 2 инструкции и смотри что там по регистрам.     Все верно это массив с индексом. Посмотрим что внутри массива.     Хехей, так это же массив с указателями на все предметы на локации. Восстановил исследуемую функцию чтобы было понятно с чем мы работали и как это использовать.     И вывод парсера всех предметов на стартовой локации, база новичков или типа того.   0x108 Item_vecPos 0x118 Item_SpawnLocation

partoftheworlD

partoftheworlD

×

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.