через какое время возрождается рошан

Тайминг рошана

09 Jul 2015 в 09:14

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:14 #1

Никогда не понимал зачем пишут 3 тайминга на рошана,ведь » однако не в фиксированный момент времени, а через 8-11 минут после своей смерти (Время выбирается случайным образом)» (http://dota2.gamepedia.com/Roshan/ru)

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:14 #2

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:15 #3

Мб для того что бы проверить его через 9-10 минут?

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:15 #4

Когда он умер
Когда пройдут 8 мин
Когда пройдут 11 мин

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:16 #5

это по гайду так.тип как стать про игроком:пиши 3 тайминга

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:16 #6

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:16 #7

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:17 #8

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:17 #9

Когда он умер
Когда пройдут 8 мин
Когда пройдут 11 мин

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:20 #10

Ты не заметил,что там 3 числа?

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:21 #11

46-дроп аегиса
49-мин время респа рошика
52-макс время респа

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:22 #12

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 09:23 #13

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

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

09 Jul 2015 в 16:12 #14

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

а можно текст просто в буфер обмена копировать (ctrl+c) (ctrl+v)

Источник

roshan

11 Mar 2017 в 09:32

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

11 Mar 2017 в 09:32 #1

через сколько времени после смерти появляется рошан?
скоро 7к,пора бы мне уже это знать.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

11 Mar 2017 в 09:35 #2

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

11 Mar 2017 в 09:36 #3

через 8 и 11 или в промежутке между 8 и 11?

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

11 Mar 2017 в 09:37 #4

Это рандомное число с 8 до 11. Всегда разное

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

11 Mar 2017 в 09:38 #5

В промежутке между 8 и 11. Там рандом. Но после второго рошана, следующий точно не появится ровно через 8, так как там ещё дополнительно накидывается от 1 до 3 минут.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

11 Mar 2017 в 13:15 #6

Рошан возрождается как и другие нейтральные монстры, однако не в фиксированный момент времени, а через 8-11 минут после своей смерти (время выбирается случайным образом). Воскрешение Рошана невозможно предотвратить(в отличие от остальных нейтральных монстров), установив варды и/или стоя в месте его воскрешения
Dota2wiki

Источник

когда рошан апается?

15 Jan 2016 в 13:16

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

15 Jan 2016 в 13:16 #1

забрали рошана, через сколько он появится?

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

15 Jan 2016 в 13:18 #2

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

15 Jan 2016 в 13:18 #3

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

15 Jan 2016 в 13:19 #4

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

15 Jan 2016 в 13:23 #5

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

15 Jan 2016 в 13:25 #6

я вот тоже этого не понимаю, в про играх они заходят на рошана за несколько секунд до появления, и всегда во время, он же с 8-11 появляется, как они так определяют точно хз, допустим рошан должен появится на 8 минуте, но они не чекают его на 8.01 чтобы убедится что он появился сейчас, а не на 10 или 11, а просто за ранее приходят и все роша на месте, в пабе же вы по 3 раза подбегаете чекнуть его.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

15 Jan 2016 в 13:37 #7

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

Источник

Визуализация времени возрождения Рошана

В данной статье рассматривается перехват функций графического API на примере DirectX 9 под x64 применительно к игре Dota 2.

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

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

Disclaimer: Автор не несет ответственности за применение вами знаний полученных в данной статье или ущерб в результате их использования. Вся информация здесь изложена только в познавательных целях. Особенно для компаний разрабатывающих MOBA, чтобы помочь им бороться с читерами. И, естественно, автор статьи ботовод, читер и всегда им был.

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

1. Вступление

Данная статья планируется как первая из цикла и дает представление о том, как можно использовать графическое API в своих целях, описывает функционал, требуемый для понимания следующей части. Вторую статью я планирую посвятить поиску указателя на список сущностей в Source 2 (также на примере Dota 2) и использовании его в связке с Source2Gen для написания «дополнительной» логики (что-нибудь по типу этого, скорее всего покажу «map hack» (обратите внимание на кавычки, о чем идет речь можно посмотреть на видео), либо автоматизации первой статьи). Третья статья планируется в виде написания драйвера, общение с ним (IOCTL), использование его для обхода защиты VAC (что-то похожее на это).

2. Для чего мне это понадобилось

Использование графического API мне понадобилось для визуальной отладки моего бота, которого я писал для Dota 2 (визуализированная информация в реальном времени очень удобна). Я являюсь аспирантом и занимаюсь реконструированием 3D головы и морфинга при помощи снимков и камеры глубины — тема довольно интересная, но для меня не самая любимая. Так как я занимаюсь этим уже пятый год (начиная с магистратуры), я понял одно — да, я неплохо изучил данную сферу, легко изучаю статьи с методами и подходами, реализую их. Но это все, сам я могу только оптимизировать очередной изученный алгоритм, сравнить его с уже изученными и реализованными и принять решение, стоит ли его использовать в определенной задаче. На оптимизации дело заканчивается, самому придумать что-то новое не получается, что для аспирантуры очень важно (новизна исследования). Начал думать — пока есть время, можно найти новую тему. В теме уже нужно хорошо разбираться (на уровне текущей) или ее можно быстро подтянуть.

Параллельно я работал в геймдеве и это, наверное, самое интересное из того, чем можно заняться программисту (личное мнение) и очень интересовался темой AI, ботов. На тот момент было две темы, в которых я неплохо разбирался — тогда я занимался построением динамического навигационного меша (клиент-сервер) и изучением сетевой части динамического шутера. Тема с динамическим нав мешем не подходила сразу — этим я занимался в рабочее время, нужно было спрашивать разрешение на ее использование в дипломе у руководства, к тому же, тема новизны была открыта — я так же неплохо изучал и реализовывал имеющиеся подходы по статьям, но в этом не было новизны. Тема с сетевой частью динамического шутера (я планировал применять ее для взаимодействия в виртуальной реальности) снова разбивалась как о то, что я занимался этим в рабочее время, так и о новизну, можно почитать цикл статей от Pixonic, где сам автор говорит, что тема эта интересная, вот только подходы изобретены 30 лет назад и особо не поменялись.

Примерно в это время OpenAI выпустили своего бота. Это конечно не 5 на 5, но это было потрясающе! Я не мог выкинуть мысли попробовать сделать бота и начал первым делом думать о том, как это использовать в качестве диссертации, о новизне, как это преподнести руководителю. С новизной в этом плане все было куда лучше — наверняка можно было придумать что-то и для двух предыдущих тем, но видимо бот заставил меня думать, цепляться, развивать и искать идеи куда сильнее. Итак, я решил сделать бота 1 на 1 (сражение на миду, как у OpenAI), презентовать его руководителю, рассказать как это круто, как тут много разных подходов, математики, а самое главное — нового.

Самое необходимое, что нужно боту на первом этапе, это знание среды, в которой он находится — состояние мира я намеревался брать из памяти игры и первый этап провел за поиском указателя на список сущностей (Entity List) и интеграции с детищем praydog`а Source2Gen — эта штука генерирует структуру движка Source2, которую берет из схем. Основной идеей и предпосылкой возникновения схем является репликация состояния между клиентом и сервером, но видимо идея разработчикам очень понравилась и они распространили ее намного шире, советую почитать тут.

У меня имелся опыт reverse engineering: делал читы для Silent Storm, делал генераторы ключей (самый интересный был для Black&White) – что такое кейген можно почитать у DrMefistO тут, выполнение комбо в Cabal Online (тут все усложнялось тем, что эту игру охранял Game Guard, охранял его из ring0 (под драйвером в режиме ядра), пряча процесс (что как минимум не дает легко внедриться в него) — подробней можно почитать тут).
Соответственно, у меня были наработки в этой сфере, бот получил доступ к окружению за планируемое время. Удивительно, как много информации сервер доты реплицирует через дельту клиенту, например, клиент имеет информацию о любых телепортах, здоровье и его изменении у энжентов (кроме Рошана, он не реплицируется) — все это в тумане войны. Хотя я и столкнулся с некоторыми трудностями — это то, о чем я собираюсь рассказать в следующей статье.
Если у вас возник вопрос, почему я не использовал Dota Bot Scripting, отвечу выдержкой из документации:

The API is restricted such that scripts can’t cheat — units in FoW can’t be queried, commands can’t be issued to units the script doesn’t control, etc.

Данный цикл статей ориентирован на новичков, которым интересна тема обратной разработки.

3. Зачем я об этом пишу

По итогу я столкнулся с множеством проблем в реализации бота со стороны ml, над которыми просидел достаточно времени, чтобы понять, что за два года до конца обучения не смогу переплюнуть мои знания и опыт в текущей теме. В Dota 2 я не играю с выхода кастомки Dota Auto Chess, свободное время теперь трачу на диплом и реверc Apex Legend (структура которой довольно схожа с Dota 2, как мне кажется). Соответственно, единственная польза от проделанной работы — публикация технической статьи на эту тему.

4. Dota 2

Приведенные принципы я планирую показывать на реальной игре — Dota 2. Игра использует античит Valve Anti Cheat. Мне очень нравится Valve как компания: очень классные продукты, директор, отношение к игрокам, Steam, Source Engine 2, … VAC. VAC работает из user-mode (ring3), он не сканирует все подряд и сравнительно с остальными античитами безобиден (от того, что делает esea (конкретно их античит) пропадает все желание пользоваться этой платформой). Я уверен, что VAC делает свою работу таким щадящим образом — не мониторит из режима ядра, не банит по железу (только аккаунт), не вставляет водяные знаки в скрины — благодаря отношению Valve к игрокам, они не устанавливают вам полноценный антивирус, как это делают Game Guard, BattlEye, Warden и прочие, потому что все это итак взламывается и в придачу тратит ресурсы процессора, которые могла бы занять игра (даже если это делается периодически), бывают ложные срабатывания (особенно у игроков на ноутбуках). Разве в PUBG, Apex, Fortnite нет wall hack, aimbot, speed hack, ESP?

Собственно о Dota 2. Игра работает с частотой 40Hz (25 ms), клиент интерполирует игровое состояние, предсказание ввода не используется — если у вас случается лаг, игра — важно даже не игра, подконтрольные юниты — полностью фризится. Сервер игровой механики обменивается с клиентом сообщениями через RUDP (надежное UDP) шифрованными сообщениями, клиент в основном отправляет ввод (если вы хостите лобби, могут отправляться команды), сервер шлет реплику игрового мира и команды. Навигация осуществляется по 3D сетке, каждая ячейка имеет свой тип проходимости. Передвижение осуществляется при помощи навигации и физики (невозможность прохождения через фиссуру шейкера, коги клокверка и тд).

Состояние мира со всеми сущностями находится в памяти в чистом виде без шифрования — можно изучать память игры при помощи Cheat Engine. Обфускация к строкам и коду не применяется.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан

5. Постановка задачи

В игре Dota 2 есть нейтральный «древний», убийство которого дает хорошее вознаграждение: опыт, золото, возможность откатить кулдауны скилов и предметов, Аегис (вторая жизнь), зовут его Рошан. Получение Аегиса может в корне перевернуть игру или дать еще большее преимущество более сильной стороне, соответственно игроки стараются запомнить/записать время его смерти, чтобы запланировать, когда нужно собраться вместе и напасть на него, либо быть поблизости для его охраны. О смерти Рошана оповещаются все десять игроков вне зависимости от того, скрыт ли он в тумане войны. Время возрождения имеет обязательные восемь минут, после которых Рошан может появиться случайным образом в интервале трех минут.

Задача следующая: предоставить игроку информацию по текущему состоянию Рошана (alive-жив, ressurect_base-возрождается базовое время, ressurect_extra-возрождается дополнительное время).

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 1 — Условия переходов между состояниями и действия при переходе

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 2 — Элементы интерфейса — лейбл, кнопка и холст

Эта единственная задача, которую я смог придумать, чтобы не требовалась работа с памятью игры и имелась хоть какая-то ценность для игрока — даже для вывода каких-либо элементарных характеристик, таких как здоровья, мана, позиции сущностей, нужно их либо предварительно найти при помощи Cheat Engine в памяти игры, что необходимо дополнительно и довольно долго объяснять, либо при помощи Source2Gen, о чем и будет следующая статья. Постановка задачи заставляет игрока следить за Рошаном, перекладывая на него много действий, что довольно неудобно — зато будет на что опереться во второй части.

Мы напишем свою injected.dll, в которой будет содержаться бизнес логика на основе MVC и внедрим ее в процесс Dota 2. Dll будет использовать нашу библиотеку silk_way.lib, которая будет содержать логику ловушек для изменения потока выполнения, логгер, сканер памяти и структуры данных.

6. Injector

Создадим пустой проект на C++, назовем NativeInjector. Основной код находится в функции Inject.

Функция получает путь и название процесса, ищет по названию процесса его Id при помощи GetProcessIdentificator.

Вкратце, GetProcessIdentificator пробегает по всем запущенным процессам и ищет процесс с соответствующим названием.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 3 — Начальное состояние процесса

Далее непосредственное внедрение библиотеки при помощи создания удаленного потока.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 4 — Создание удаленного потока

Обязательно в конце закрываем обработчик процесса функцией CloseHandle, можно также освободить выделенную память. Наш инжектор готов и ждет, когда мы напишем бизнес логику в injected.dll с библиотекой silk_way.lib.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 5 — Завершение внедрения библиотеки

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

7. Silk Way

Приступим к реализации silk_way.lib — статической библиотеки, которая содержит структуры данных, логгер, сканер памяти и ловушки. По сути я взял маленькую часть своих наработок, то, что можно объяснить проще всего, что не слишком завязано на остальное, но в тоже время решает поставленную задачу.

7.1. Структуры данных.

Вкратце про структуры данных: Vector – классический список, время вставки и удаления O(N), поиск O(N), память O(N); Queue – циклическая очередь, время вставки и удаления O(1), поиск отсутствует, память O(N); RBTree — красно-черное дерево, время вставки и удаления O(logN), поиск O(logN), память O(N). Я предпочитаю хешу, который используется для реализации словарей в C# и Python, красно-черные деревья, которые использует стандартная библиотека C++. Причина — хеш сложнее реализовать правильнее по сравнению с деревом (примерно каждые пол года нахожу и пробую разновидности хешей), и обычно хеш занимает больше памяти (хотя работает быстрее). Данные структуры используются для создания коллекций как в бизнес логике, так и в ловушках.

Я стараюсь не использовать структуры из стандартной библиотеки и реализую их сам, конкретно в нашем случае это не имеет значения, но это важно, если ваша dll будет подвергнута дебагу или сборка находится в открытом виде (это скорее касается коммерческих читов, что мы с вами осуждаем). Все структуры я советую писать самим, это дает вам бОльшие возможности.
Как пример, если вы делаете игру и не хотите, чтобы «школьники» сканировали ее при помощи Cheat Engine, можно сделать обертки для примитивных типов и хранить в памяти зашифрованное значение. На самом деле это не спасение, но может отсеять некоторую часть тех, кто пытается прочитать и изменить память игры.

7.2. Логгер

Реализован вывод в консоль и запись в файл. Интерфейс:

Реализация для вывода в файл:

Реализация для вывода в консоль однотипна. Если мы хотим использовать логирование, необходимо определить интерфейс ILogger*, объявить нужный логгер, вызвать функцию Log c требуемым форматом, например:

7.3. Сканер

Сканер занимается тем, что выводит значение памяти, на который указывает переданный указатель и производит сравнение с образцом в памяти. Функционал сравнения с паттерном будет рассмотрен позже.

Реализация заголовочного файла:

Реализация файла источника:

Для использования необходимо определить интерфейс IScanner*, объявить нужный сканер и вызвать функцию PrintMemory, где задать титул, указатель и длину, например:

7.4. Ловушки

Самая интересная часть библиотеки silk_way.lib. Ловушки (hook) служат для того, чтобы изменять поток выполнения программы. Создадим исполняемый проект с названием Sandbox.

Класс Device наследуется от интерфейса IUnknown, наша задача перехватить вызов функций Present и EndScene любого экземпляра Device, в приемнике вызвать оригинальные функции. Мы не знаем места в коде, где и зачем вызываются эти функции, в каком потоке.

Смотря на функции Present и EndScene видно, что они виртуальные. Виртуальные функции нужны для того, чтобы переопределять поведение родительского класса. Виртуальные функций, как и не виртуальные, представляют из себя указатель на память, в которой записаны операционные коды (opcode) и значения аргументов. Так как виртуальные функции отличаются у наследников и родителей, они имеют разные указатели (это совершенно разные функции) и хранятся в Таблице виртуальных методов (VMT). Эта таблица хранится в памяти и представляет собой указатель на указатель класса, найдем ее для Device:

VMT хранит указатели на виртуальные функции, если мы захотим наследоваться от Device, наследник будет содержать свою VMT. VMT хранит указатели на функции последовательно с шагом, равным размеру указателя (для x86 это 4 байта, для x64 – 8), соответствуя порядку определения функции в классе. Найдем указатели на функции Present и EndScene, которые располагаются на третьем и четвертом месте:

Важно также то, что указатель на метод класса должен первым аргументом содержать ссылку на экземпляр класса. В C++, С# это прячется от нас, а компилятор об этом знает — в Python явно указывается self первым параметром в методе класса. Подробней о соглашение о вызове (calling convention) тут, искать нужно thiscall.

Чтобы разобраться в наборе этих кодов можно посмотреть таблицу, либо использовать имеющиеся дизассемблеры. Мы возьмем готовый дизассемблер — hde(hacker disassembler engine). Также для сравнения можно глянуть на distorm и capstone. Передайте указатель на функцию любому дизассемблеру и он скажет, что за опкоды в ней используются, значения аргументов и прочее.

7.4.1 Opcode Hook

Теперь мы готовы перейти непосредственно к ловушкам. Мы рассмотрим Opcode Hook и Hardware Breakpoint. Самые распространенные ловушки, которые я советую реализовать и поизучать.

Наверное самой часто используемой и простой ловушкой является Opcode Hook (в статье с перечислением ловушек она называется Byte patching) — заметьте, она легко распознается античитом при неумелом использовании (без понимания, как работает античит, без знания, какую область и секцию памяти он сканирует в текущий момент и прочего бан не замедлит себя ждать). При умелом использовании это прекрасная ловушка, быстрая и простая для понимания.
Если при чтении статьи вы параллельно воспроизводите код и находитесь в режиме Debug, переключитесь в Release — это важно.

Итак, напомню, нам необходимо перехватить выполнение функций Present и EndScene.
Реализуем перехватчики — функции, куда мы хотим передать управление:

Подумаем над абстракциями, которые нам нужны. Нам требуется интерфейс, который даст возможность поставить ловушку, убрать ее и предоставить информацию о ней. Информация о ловушке должна содержать указатель на перехватываемую функцию, функцию приемник и трамплин (то, что мы перехватили функцию не значит, что она больше не нужна, мы также хотим иметь возможность использовать ее — трамплин поможет вызвать исходную перехваченную функцию).

Интерфейс IHook предоставляет нам такие возможности. Мы хотим, чтобы когда любой экземпляр класса Device вызывал функции Present и EndScene (то есть указатель RIP переходил на эти адреса), выполнялись соответственно наши функции PresentHook и EndSceneHook.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 6 — Начальное состояние памяти, исполнение заходит в перехватываемую функцию

Теперь мы хотим, чтобы RIP (красная стрелка) перешла с source на начало destination. Как это сделать? Как уже написано выше, участок памяти source содержит опкод, который процессор выполнит, когда до source дойдет выполнение. По сути, нам нужно перепрыгнуть из одной части в другую, перенаправить указатель RIP. Как вы уже могли догадаться, есть опкод, который позволяет переводить управление из текущего адреса в желаемый, называется эта мнемоника JMP.

Прыгать можно как напрямую на нужный адрес, так и относительно текущего адреса, эти прыжки можно отыскать в табличке – ff и e9 соответственно. Создадим структуры для этих инструкций:

Инструкция относительного прыжка короче, но есть ограничение — unsigned int говорит о том, что прыгнуть можно в пределах 4,294,967,295, что для x64 процесса не достаточно.
Соответственно, адрес функции приемника destination может легко перевалить это значение и находиться за пределами unsigned int, что вполне возможно для x64 процесса (для x86 все намного проще и можно ограничиться как раз этим самым относительным прыжком для реализации Opcode Hook). Прямой прыжок занимает 14 байт, для сравнения относительный — лишь 5 (мы упаковали структуры, обратите внимание на #pragma pack(push, 1)).

Нам нужно переписать значение по адресу source на одну из этих прыжковых инструкций.
Перед тем, как ловить функцию, следует изучить ее — проще всего это сделать при помощи дебагера (дальше я покажу, как это делать при помощи x64dbg), или дизассемблера. Для Present мы уже вывели 30 байт от ее начала, инструкция 48 89 4c 24 8 занимает 5 байт.
Давайте реализуем относительный прыжок. Мне больше нравится этот вариант во многом из-за длины инструкции. Идея в следующем: мы заменяем первые 5 байт исходной функции, пресохранив измененные байты, заменяем их относительным прыжком на адрес инструкции, который лежит в пределах unsigned int.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 7 — Исходные 5 байт функции source заменяются относительным прыжком

Что нам дает прыжок на выделенную память (фиолетовая область), как этим действием мы приблизили себя к передачи управления на destination? В выделенной нами памяти располагается прямой прыжок, который и будет перемещать RIP на destination.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 8 — Переключение RIP на функцию приемник

Осталось придумать, как вызвать пойманную функцию. Нам нужно выполнить затертые инструкции и начать выполнение с нетронутой части source. Поступим следующим образом — сохраним поврежденные инструкции в начало trampoline, запомним, сколько байт было повреждено и прыгнем прямым прыжком на source + corruptLen, к «здоровым» инструкциям.

Выполнение сохраненных инструкций, затертых относительным прыжком:

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 9 — Использование трамплина для вызова перехваченной функции

Дальнейшее выполнение инструкций, которых не коснулось затирание:

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 10 — Продолжение исполнения инструкций перехваченной функции

Очень важный момент заключается в том, что нам нужно выделить память под трамплин и relay, в которых мы будет хранить инструкции для перенаправления потока от source к destination и адрес на эту память должен быть в пределах, на которые может себе позволить прыгнуть относительный прыжок (unsigned int).

Данный функционал реализует функция AllocateMemory.

Идея проста — будем идти по памяти, начиная от определенного адреса (в нашем случае указателя на source) вверх и вниз, пока не найдем подходящий по размеру свободный кусок.

Вернемся к функции SetHook. В выделенную память копируем потертые байты из source и после сразу вставляем прямой прыжок на source + corrupt, чтобы продолжить выполнение с неповрежденных инструкций.

Далее идет установка указателя relay, который отвечает за перенаправление потока выполнения на destination путем прямого прыжка на адрес приемника. В конце изменяем source – выставляем права на запись в то место памяти, где находится функция и заменяем первые 5 байт на относительный прыжок, ведущий на адрес relay.

Ловушку мы установили, но ее также нужно уметь убирать. Ломать — не строить, идея простая — вернем потертые байты source, удалим запись о ловушке из коллекции, а выделенную память освободим:

Тестируем работу. Сразу изменим наши приемники так, чтобы они могли вызывать перехваченные функции при помощи трамплина:

Работает. Дополнительно проверить можно в x64dgb.

Помните, вначале я попросил вас работать в Release сборке? Теперь перейдем в Debug и запустим программу. Программа падает… Ловушка срабатывает, но попытка вызвать трамплин вызывает исключение, которое говорит, что адрес, по которому мы вызываем трамплин, совсем не на выполнение. Что мы упустили? В чем проблема Debug сборки? Запускаем и смотрим на опкод функции Present:

При запуске в x64dbg можно увидеть следующее.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 11 — Инструкции Debug сборки

В Debug опкод изменился, теперь компилятор добавляет относительный прыжок e9 f4 36 0. В прыжок оборачиваются все функции, включая main и точка входа в программу mainCRTStartup. Другой опкод, ну и ладно, он должен был скопироваться в трамплин, при вызове трамплина должен быть вызван этот относительный прыжок, далее прямой прыжок на неповрежденную часть source.

Тут становится понятно, что все делается как мы и реализовали, только вот относительный прыжок на то и относительный, что его выполнение от разных адресов, source и trampoline, выставляют RIP на совершенно разные значения.

По моему скромному опыту, реализация случая с относительным прыжком покрывает 99% использования. Есть еще несколько опкодов, которые следует обрабатывать отдельно. Помните, что перед тем, как ставить ловушку на функцию, следует не полениться и изучить ее. Я не буду забивать вам голову и дописывать функционал до 100 процентного варианта (опять же, по моему скромному опыту), если вам это нужно или интересно, вы можете посмотреть, как устроены такие библиотеки и конкретно какие еще случаи они проверяют — это будет легко сделать, если вы разобрались с тем, о чем тут идет речь.

Относительный прыжок действительно встречается довольно часто, поэтому я предлагаю реализовать его. Относительный прыжок состоит из опкода e9 и значения, на которое относительно текущего адреса нужно прыгнуть. Соответственно, можно просто узнать, куда нужно прыгать, и прыгнуть туда сразу из трамплина прямым прыжком. Даже если мы встретим там новый относительный прыжок — он уже будет от правильного адреса.

Если дизассемблер возвращает информацию о том, что опкод данной команды равен e9, мы вычисляем адрес, на который нужно прыгнуть (ULONG_PTR ripPtr = (ULONG_PTR)pSource + context.len + (INT32)context.imm.imm32), и записываем адрес в трамплин как значение аргумента прямого прыжка.

Также замечу, что в многопоточной среде может возникнуть ситуация, когда в момент установки/снятия хука один из потоков может начать выполнять функцию, которую мы ловим — в итоге процесс упадет. Частично как с этим бороться будет рассказано в Hardware Breakpoint.

Если вам нужно проверенное средство, вам хочется быть уверенным, что ваша ловушка сработает, у вас нет своих наработок и вы не хотите изучать пролог функции — используйте готовые решения, например Microsoft предлагает свою библиотеку Detour. Я не использую такие библиотеки и пользуюсь самописным решением по ряду причин, поэтому не могу что-то посоветовать, могу лишь назвать те библиотеки, изучением которых занимался в целях открыть что-то новое и взять себе на вооружение: PolyHook, MinHook, EasyHook (особенно если нужны хуки на C#).

7.4.2. Hardware Breakpoint

Сам я не понимаю, в чем проблема, почему VAC не затирает DR регистры и никак это не отслеживает/отслеживал, если кто-то может дать ответ на этот вопрос, я буду очень рад. Даже если за HWBP сейчас банят, если кто-то может объяснить, почему не банили столько времени, я буду рад, так как не понимаю сложности очистки регистров DR0-DR7 для каждого контекста потока.

HWBP использует специальные регистры процессора для прерывания выполнения потока. Если контекст потока содержит установленные определенным образом регистры DR0-DR7 и RIP переходит на один из четырех адресов, хранящихся в DR0-DR3, вылетает исключение, которое можно поймать, по типу исключения и состоянию контекста определить, на каком адресе управление кинуло исключение и сделать вывод — ловушка это или нет. Существенное ограничения этого подхода в том, что использовать можно только четыре функции единовременно и выставлять их необходимо для каждого потока отдельно, что приводит к неудобствам в случае, если ловушка установлена и создается новый / пересоздается старый поток, который вызывает ловушку. Это не является особым препятствием и правится перехватом функции BaseThreadInitThunk, ограничение в использование 4 ловушек лично мне не особо мешали. Если вам критично количество хуков, обратите внимание на подход PageGuard.

Итак, задача стоит та же самая — мы находимся в песочнице (проект Sandbox), необходимо перехватить методы класса Device Present и EndScene, в которых вызвать исходные методы. Мы уже имеем готовый интерфейс для ловушек — IHook, давайте разберемся с работой «железных» точек останова.

Принцип такой: есть четыре «рабочих» регистра DR0-DR3, в которые можно записать адрес, в зависимости от настройки управляющего регистра DR7 при попытке записи, чтения или выполнения по заданному адресу произойдет исключение с типом EXCEPTION_SINGLE_STEP, которое должно быть обработано в предварительно зарегистрированном обработчике. Можно использовать как SEH обработчик, так и VEH – будем использовать последний, так как он имеет больший приоритет.

Реализуем эту идею:

Подробней о том, что представляет из себя DR6 и DR7, как и о подходе PageGuard могу посоветовать книгу Gray Hat Python: Python Programming for Hackers and Reverse Engineers. Вкратце, DR7 включает/отключает использование «рабочего» регистра — даже если какой-либо из регистров DR0-DR3 будет содержать адрес, но в DR7 флаг соответствующего регистра будет отключен, работать точка останова не будет. Также DR7 задает тип работы с адресом, при котором нужно кидать исключение — прочитался ли адрес, была ли произведена запись или адрес используется для выполнение инструкции (нас интересует последний вариант).

Снятие ловушки тоже довольно простое и производится через управляющий регистр DR7.

Осталось разобраться с потоками — ловушка должна быть расставлена для тех потоков, которые вызывают перехватываемую функцию. Мы не будет заморачиваться на этот счет.

Приведенный код обходит все видимые процессы и ищет текущий процесс. В найденном процессе для очередного потока получаем обработчик потока, находим один из четырех свободных регистров и устанавливаем ловушку. Стоит обратить внимание на функции Freeze и Unfreeze – это то, о чем говорилось в Opcode Hook по поводу многопоточности — они полностью останавливают выполнение потоков данного процесса (кроме текущего), чтобы не произошло ситуации, когда один из потоков заходит в перехватываемую функцию.

Аналогичное нужно реализовать в функции снятия ловушки.

Осталось добавить обработчик исключений VEH. Добавление и удаление производится функциями AddVectoredExceptionHandler и RemoveVectoredExceptionHandler из любого потока.

Обработчик должен проверять тип исключения (нужен EXCEPTION_SINGLE_STEP), проверять соответствие адреса, на котором возникло исключение с тем, что есть в регистрах и в случае обнаружения такого адреса переставляет указатель RIP на адрес приемника. Состояние стека сохраняется, так что при дальнейшем выполнении приемника все параметры на стеке будут целы.

Реализуем описанный обработчик в песочнице:

По идее все готово, запускаем программу, ожидая точно такой же работы, как у OpcodeHook.
Этого не происходит, наша программа зависает — точнее она постоянно заходит в PresentHook и в тот момент, когда должен вызываться трамплин, функция вызывается снова. Дело в том, что «железная» точка останова никуда не делась, так как при вызове трамплина (который в случае «железных» точек останова указывает на исходную функцию) мы снова тревожим тот же адрес и вызываем исключение. Решение следующее — уберем точку останова при ее обнаружении в обработчике для конкретного потока, а в нужный момент снова выставим. Местом обновления выберем момент завершения функции приемника.

Реализовано это следующим образом — в обработчике вместе со снятием точки останова добавляется отложенная команда, смыслом которой и является обновление точки останова в указанном потоке. Команда запускается в конце функции приемника.

Представим визуально работу «железных» точек останова.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 12 — Начальное состояние

Ставим ловушку, добавляем VEH обработчик, ждем, когда управление дойдет до функции-источника:

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 13 — Стадия подготовки перехвата

Возбуждается исключение, вызывается обработчик, который перенаправляет RIP на приемник и сбрасывает точку останова:

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 14 — Перенаправление потока выполнения на функцию приемник

На этом тему ловушек можно закончить, статическая библиотека silk_way.lib готова. Из своего опыта могу сказать, что довольно часто пользуюсь OpcodeHook, VMT Hook, Forced Exception Hook (наверное, самая «геморойная» ловушка), HardwareBreakpoint и PageGuard (когда время выполнения не критично, разовые перехваты).

8. Архитектура логики

Основа логики представлена в виде MVC (model-view-controller). Все основные сущности наследуются от интерфейса ISilkObject.

8.1. Модель

В библиотеке при разработке бота сначала я реализовал ECS (про данный подход можно почитать тут и тут). Когда я понял, что запуск бота с реальными игроками довольно долгое занятие, я написал симуляцию, где тестировались ml библиотеки (с трехмерной сеткой для навигации (Dota 2 как раз использует 3D сетку для навигации) и упрощенной 2D физикой для бодиблока). Когда надобность в симуляции пропала и я придумал, как и что логировать, какую информацию собирать в ходе сражения, в ECS отпала всякая необходимость и модели стали просто содержать словарь компонент (для представления примерно так, как сделано у ребят из SkyForge, раздел «Аватары и мобы»), которые содержали, по сути, обертки над структурами из Source2Gen. Для данной статьи я не стал переносить эту реализацию в целях упрощения материала. Модель содержит Schema, в которой хранится ее описание (этот момент упрощен и в данной реализации модель не создается по схеме, схема лишь описывает ее (хранит в себе предустановленные значения, которые можно захардкодить) — это можно сравнить с хранением наполнения игры в xml/json).

Схематично устройство модели можно представить так:

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 15 — Схематичное представление Модели

Схема включает в себя описание конкретной модели и содержит контекст, которым может пользоваться модель.

8.2. Представление, Состояние Представления и Контроллер

Про Представление, Состояние Представления и Контроллер особо сказать нечего, реализация схожа с Моделями. Они также состоят из схемы и контекста. Для решения задачи для Представления реализованы Canvas, ViewCollection, Label и Button, для последних двух также реализованы состояния, соответствующие состояниям, в которых находится Рошан.

8.3. Фабрика

Объекты создаются с использованием фабрики. Фабрики используют в качестве ключа тип интерфейса, переводя его в строку посредством typeid(T).raw_name(). Вообще, так делать плохо, почему и как правильно можно почитать у Andrei Alexandrescu, Modern C++ Design: Generic Programming. Реализация фабрики:

Перед использованием фабрики для построения объектов нужно произвести регистрацию.

8.4. События

Представление узнает об изменении Модели посредством событий. Получать обратную связь можно в методы класса и обычные функции.

Если объект хочет сообщать о происходящих внутри него событиях, необходимо добавить для каждого события IEvent*. Другой объект, которому интересны события, происходящие внутри этого объекта, должен создать ICallback* и передать его внутрь IEvent* (подписаться на событие).

Основные примитивные типы (SILK_INT, SILT_FLOAT, SILK_STRING, …) реализованы в Core.h.

9. DirectX 9

Чтобы перехватить виртуальный метод класса (как и не виртуальный) нам нужен экземпляр этого класса, любой экземпляр. При помощи экземпляра мы достанем Таблицу виртуальных методов и получим нужные указатели на функции. Самый простой способ найти экземпляр класса — создать его самим.

Для этого нам потребуется создать объект с интерфейсом IDirect3D9 при помощи функции Direct3DCreate9, а само устройство создадим при помощи этого объекта вызовом метода CreateDevice. Мы можем вызвать эти функции напрямую из библиотеки DirectX, но ради закрепления материала вызовем их через указатели. Как видно из d3d9.h, Direct3DCreate9 является обычной функцией и указатель на нее может быть получен через GetProcAddress (точно так же, как мы делали в NativeInjector для получения указателя на LoadLibrary).

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 18 — описание CreateDevice в d3d9.h

Создадим экземпляр IDirect3D9:

При помощи IDirect3D9 мы можем создать девайс вызовом pD3D->CreateDevice(. ). Для получения указателя на нужные функции из VMT, нам нужно узнать порядок определения данных методов.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 19 — Поиск индекса метода CreateDevice интерфейса IDirect3D9

Получаем 16-й индекс. Помимо CreateDevice нам также нужны методы Release и GetAdapterDisplayMode.

Отлично, мы создали устройство DirectX 9, теперь нужно понять, какие функции используются для отрисовки сцены, что нам нужно перехватывать. Нам нужно ответить на вопрос: «Как DirectX 9 показывает нам сцену?». Для показа сцены используется функция Present. Стоит также ввести такие понятия, как front buffer (буфер, который хранит то, что отображается (продолжительное действие) на экране), back buffer – содержит то, что готово для отображения и готовится стать front buffer, swap chain – собственно набор буферов, которые поочередно сменяются (flipping) с front на back (DirectX 9 имеет только 1 swap chain). Перед тем, как вызвать Present, вызывается пара функций BeginScene и EndScene, где можно изменять back buffer.

Давайте перехватим две функции (на самом деле для выполнения бизнес логики нам хватит и одной): EndScene и Present. Для этого посмотрим на расположение этих функций в классе IDirect3DDevice9

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 20 — Объявление интерфейса IDirect3DDevice9

Объявим указатели со следующими сигнатурами функций:

Объявим ловушку сразу с обработчиком ошибок, так как HardwareBreakpoint на самом деле наш единственный реализованный безопасный вариант перехвата, который не отслеживает VAC (потестить можно и с Opcode Hook, но аккаунт скорее всего улетит в бан):

Захукаем обозначенные функции любой из наших двух ловушек:

В EndScene создается контроллер бизнес логики. После создания вызывается обновление контроллера, где обновляется вся логика.

Замечу, что сейчас мы реализовали работу с DirectX 9. Если мы хотим делать какой нибудь мод, чит и тд — необходимо поддерживать все четыре API. Это оправдано, если на вооружении уже есть любимые библиотеки, заготовки для UI, в ином случае можно использовать другой способ — функционал, который использует движок для отрисовки игры.

Также стоит сказать, что вызов обновления логики из EndScene() не лучший вариант — можно найти периодические вызовы функций движка или вызывать логику в своем потоке. Если все же вызов из EndScene устраивает, лучше делать это при помощи lockstep.

Теперь мы реализовали все, что планировали.

Тестировать это дело лучше всего на DirectX SDK, там куча примеров работы, от DirectX 9 до DirectX 11. Если в качестве закрепления материала вы захотите реализовать перехват для DirectX 11, советую просто выбрать какой-нибудь пример с исходным кодом из SDK, разобраться как он работает (это даст понимание, какие функции отвечают за отрисовку и что нужно перехватывать) Если вы новичок, сделать это будет не просто, так как примеры используют DXUT, но все же это реальное окружение, боевые условия и вывод статистики — будет сразу видно, насколько просаживается FPS при использовании ловушки.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 21 — Внедрения в пример из DirectX SDK под названием StateManager.exe

Теперь можно создать фейковый аккаунт в стиме и заинжектить injected.dll в процесс Dota 2. Скажу сразу, я не знаю, как на текущий момент обстоит дело с «железными» точками останова — за использование Opcode Hook (так, как делаем это мы в текущем виде) вы точно получите бан. Я занимался этим около полугода назад — бана за Hardware Breakpoint не было, какова ситуация на текущий момент сказать не могу. Перед подготовкой статьи я взял два аккаунта и попробовал Opcode Hook и HWBP на них, первый улетел в бан (прошло около 2-х недель), второй нет (прошло 3 недели). Но все равно нет никаких гарантий, что бана не будет в будущем. Потом не обижайтесь, если случайно произведете внедрение со своего основного аккаунта или забудете перелогиниться на фейковый — тут уже следите за собой сами и будьте внимательны.

Внедрение в режиме 1×1.
через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 24 — Внедрение в матч

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

Наше внедренное UI содержит только текстовый лейб и кнопку, реализованные на чистом DirectX 9 — это все, что требуется для решения поставленной задачи. Вы же можете реализовать как на чистом API, так и с использованием готовых библиотек сложные таблицы, красивые меню и диаграммы — в общем, UI любой сложности. Разумеется, не только 2D.

10. Использование функций движка

Реализовывать один и тот же функционал для каждого API довольно муторно, разработчики делают удобные обертки, предоставляя функции для рисования, UI и прочее, что использует непосредственно игра. Также Valve предоставляет для Dota 2 API на Javascript и Lua. Делается это для того, чтобы облегчить жизнь модерам и гейм дизайнерам, для которых C++ сложен (даже не сам C++, а правильное использование в контексте движка). Тут есть и функции по отрисовке, и по логике игры — можно прописывать поведение юнита, например, подбор предметов, использование скилов и прочее. Собственно, при помощи этого и пишутся кастомки.

Нас будет интересовать функция DoIncludeScript, которая позволяет запускать свои скрипты на Lua и использовать там Scripting API. Я не использовал ее в своем проекте, так как не видел в ней ценности, пользуясь функциями напрямую из C++, идею ее использования я увидел у or_75 и решил включить в статью. Это познакомит вас с тем, что будет во второй части и сэкономит в ней место, не придется объяснять определенные моменты работы отладчика.

Приступим. Задача стоит следующая: необходимо найти указатель на функцию DoIncludeScript, которая принимает название скрипта и хендлер, изучить ее. Искать функцию мы будем при помощи сканера из нашей библиотеки silk_way.lib. Функции, как мы уже выяснили, кодируются в памяти при помощи таблицы опкодов — давайте изучим эту функцию и попробуем выявить ее шаблон хранения в памяти. Сейчас сканер не обладает нужным функционалом, нам нужна возможность поиска шаблона в памяти процесса.

Для ускорения поиска мы будем искать паттерн не по всей памяти процесса, а в конкретном модуле (наша функция лежит в client.dll, это будет видно в отладчике и рассмотрено ниже). Модуль будем искать при помощи tlHelp32 по названию путем перебора всех модулей процесса, для чего создадим функцию нахождения модуля в текущим процессе GetModuleInfo.

Парсим строку, для удобства переведем паттерн из строкового представления в список unsigned char значений, установим флаги пропускаемых байт.

Ядро поиска реализовано в функции FindPattern, где по полученной информации о модуле устанавливается начальный и конечный адрес поиска. Информация о памяти, по которой будет произведен поиск, запрашивается функцией VirtualQuery, к памяти есть ряд требований — она должна быть занята (будет ошибкой искать в свободной памяти), память должна быть читаемая, выполняемая и не содержать флага PageGuard:

При каждом останове (их будет 10-15) продолжаем выполнения при помощи Run (F9). Когда игра запустится, мы увидим меню и можем приступить к исследованию. После запуска игры выполним поиск по строкам (Search for->All Modules->String References), установим фильтр “DoIncludeScript”.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 25 — Поиск по строкам в памяти процесса игры

Перейдем в дизассемблер (вкладка CPU) двойным нажатием на первый результат. Это и будет наш отправной адрес, так как он находится в client.dll, остальные результаты ведут в server.dll и animationsystem.dll.

Построим граф вызовов с полученного адреса.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 26 — Граф вызовов

После декомпиляции находим точку входа, где используется DoIncludeScript — четвертая нода графа. Собственно, сама функция.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 27 — функция DoIncludeScript

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 28 — Граф вызовов из DoIncludeScript

Декомпиляция использования функции показывает следующий код и место его вызова (декомпилирование производим из графа, не из дизассемблера).

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 29 — Декомпиляция вызова функции DoIncludeScript

Создадим скрипт на Lua silk_way.lua, положим его в «. \Steam\steamapps\common\dota 2 beta\game\dota\scripts\vscripts».

Данный скрипт обходит все сущности и выводит в соответствии с ее позицией координаты.

Объявим функцию, используя приведенную выше документацию и декомпилированный код из Рисунка 29.

После внедрения увидим информацию о позиции сущностей игры.

через какое время возрождается рошан. Смотреть фото через какое время возрождается рошан. Смотреть картинку через какое время возрождается рошан. Картинка про через какое время возрождается рошан. Фото через какое время возрождается рошан
Рисунок 30 — Результат внедрения

Теперь мы умеем запускать свои скрипты. Но они исполняются на Lua, а допустим, событие о том, что Рошан умер, нам нужно в C++ коде (так как на нем у нас написана основная логика), как быть? Придется аналогичным образом найти указатели на нужные функции (как мы это сделали для DoIncludeScript), функции движка и другой интересующий нас функционал при помощи Source SDK и Source2Gen. Но об этом в следующей части, где мы найдем указатель на список сущностей и напишем более приближенную к механике игры логику. Если же вам хочется все и сразу, можете попробовать, прилагаю для вас в качестве помощи эту, эту, эту и эту ссылки.

11. Заключение

В погоне за простотой изложения я мог что-то пропустить, опустить различные случаи, которые посчитал недостойными внимания, либо наоборот, раздуть объяснение — если внимательный читатель найдет такие ошибки, буду рад их исправить и послушать замечания. Исходный код можно найти по ссылке.

Спасибо всем, кто потратил время на прочтение статьи.

Источник

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *