-
Автор темы
- #1
Более форматированная версия -
Всем привет! Уже около шести месяцев я занимаюсь разработкой своего экстернал чита Spurdo как хобби в свободное от работы время. Хочу поделиться с вами проблемами, с которыми я столкнулся, и размышлениями о том, как их решить.
Это мой личный опыт, который, скорее всего, применим к большинству масс-маркета. Всё это можно было бы разделить на три статьи и рассказать о каждом более подробно, но мне кажется, что этот формат более подходящий.
Case №1: Проблема поддержки полноэкранного режима
Первый способ
В большинстве масс-маркет читов для Counter-Strike 2 существует проблема поддержки полноэкранного режима. Например, X1 утверждает, что эта проблема подвластна всем внешним читам и предлагает использовать масштабирование с помощью видеокарты.
В целом, это был первый вариант решения проблемы, но есть некоторые "но":
Второй способ решения этой проблемы - это просто перехватить оверлей легитимной компании, которая уже исправила за вас эти проблемы =). Фактически, этот способ является стандартом индустрии и практикуется в большинстве читов (в основном в играх, защищенных EAC / BE).
Перехватить можно как минимум двумя способами:
Злоупотребление DWM
В названии указана страшная аббревиатура DWM. Что же это такое? Desktop Window Manager (DWM) - это часть операционных систем Windows. Он отвечает за управление окнами на рабочем столе. Раньше его функция состояла только в том, чтобы обеспечить сглаживание окон и прочие визуальные эффекты, такие как Aero Glass.
С выходом Windows 8, DWM стал обязательным компонентом Windows, в тот же период времени они добавили полную поддержку глубины окон, т.е. сделали условные слои, как в фотошопе, которые позволяли полноценно накладывать одни окна на другие. Слои поделены на группы по порядку отрисовки на экране.
Вот вам enum с названиями этих групп, они, кстати, называются ZBID:
ZBID_DESKTOP - самая первая группа глубины окон, именно в ней рисуются все окна!
Вы могли заметить, что я к коду приложил порядок отрисовки на экране, и последним на нём рисуется ZBID_UIACCESS. Если мы создадим окно с этим атрибутом, то мы сможем просто взять и нарисовать наш оверлей поверх экрана как системный. Но как же его создать?
Вы можете воспользоваться недокументированной функцией CreateWindowInBand из user32.dll, которая подозрительно сильно похожа на CreateWindowEx, вот её прототип:
При разработке я не заметил никаких минусов этого подхода, но в теории их всего 2:
В какой-то момент вы столкнетесь с проблемой: ваш чит может начать лагать у пользователей. И эти 15 миллиардов кадров, которые вы видите на своем i9-15900k 400w, ваши пользователи могут не увидеть на своем Core 2 Duo. Что же делать? Конечно, оптимизировать. Я ведь не просто так это написал в заголовке кейса. Но нам нужно ознакомиться с мемом про оптимизацию, прежде чем приступить.
Теперь можно.
Побольше swap context
У вас же есть свой драйвер, верно??? А может быть, и гипервизор?? Хипстеры. Даже если вы ответили "нет", в любом случае операция изменения памяти (независимо от того, чтение это или запись) занимает очень много процессорного времени. Но знаете что?
Если вы прочтете не 2 байта за один вызов, а 50 тысяч, то операция займет столько же времени. И вот на этом основан простой метод оптимизации. Почему бы нам не объединить чтение каждого оффсета в чтение одного большого класса? В своем чите я использовал преимущества схемы в Counter-Strike 2, написал короткий подсчет размера всех нетворк-классов и написал кодген под них.
В итоге удалось сократить количество вызовов чтения памяти с 60+ до 10-13. А что по цифрам?
До оптимизации: 1300000-1600000ns
После: 70000-110000
Разница: 1454.54% - 1857.14%
И самое главное, что при необходимости взять что-то из класса игрока количество вызовов чтения памяти не будет расти, что очень важно для большого экстернал-чита
Таймеры, часы, где же суть?
Читатель, погружаемся в лор. Представь: ты создаешь свою OC, уже сделал потоки, процессы, но появилась проблема - они все время работают и нагружают систему, даже когда не активны. Тебе нужно добавить механизм, который бы контролировал их состояние. Хорошо, придумали: в процессоре есть прерывания, будем вызывать их через каждый n-интервал и будить потоки, которые находились в спящем режиме. Но возникает проблема: что, если один поток требует одного интервала, а другой - другого? Например, чтобы включать музыку, нужно быстро обрабатывать все и минимальное время на сон, а в автозаварке кофе можно поставить больший таймер.
Если тебе пришло в голову поставить для всей системы самый низкий интервал среди приложений, поздравляю с прохождением теста - ты индус из Microsoft. Подход, казалось бы, работает, но нагружает систему, когда это не нужно.
В Windows получить текущий интервал можно с помощью утилиты clockres с System Internals:
Причем здесь вообще экстерналы и оптимизация? Чтобы процессор не нагружался и правильно ограничивать FPS, вы используете Sleep или std::this_thread::sleep_for - не имеет значения. Так вот, когда ваш поток засыпает на n ms, он может проснуться ровно по таймеру, а может и позже, ведь планировщик вызывается интервалами. Если ваш сон дольше, чем один интервал, то он будет перенесен на другой, то есть:
С этим можно бороться следующими способами:
Буду честен, над этой проблемой мы в проекте провели не одну неделю, перепробовали всё: начиная со сменой движка отрисовки, заканчивая переписыванием полностью кодовой базы. В один момент, спустя месяцы, я снова решил попробовать свои силы в этой проблеме. При диалоге и рассуждении с моим хорошо знакомым seniusz, ко мне пришло озарение, что это Windows мне что-то портит. Спустя пару минут гугления обновлений Windows, я нашёл то самое! Чтобы исправить маленький FPS в оверлее, вам нужно сделать две вещи:
К сожалению, проблема до сих пор остается частично. Что-то в Windows обрезает FPS ровно в два раза, выделяя больше ресурсов на игру. Если вы знаете, как это решить - отпишите мне, буду благодарен!
Case №4: Защищайтесь сударь
В этом кейсе нет конкретики. В большинстве внешних читов, которые мне довелось протестировать, я замечал много проблем с безопасностью от анти-чита (например, X1, Pellix, PUSSYCAT, AIMSTAR и т.д.).
Основная проблема: мало кому нужен чит, который не способен обеспечить защиту от анти-чита.
Вывод
В разработке экстернал читов есть множество проблем, но все они решаемы. Главное думать головой, и пытаться понять что делаешь.
До скорой встречи, Make cheats, not war!
Мой блог ( зачем он здесь?? ) -
Источники:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Всем привет! Уже около шести месяцев я занимаюсь разработкой своего экстернал чита Spurdo как хобби в свободное от работы время. Хочу поделиться с вами проблемами, с которыми я столкнулся, и размышлениями о том, как их решить.
Это мой личный опыт, который, скорее всего, применим к большинству масс-маркета. Всё это можно было бы разделить на три статьи и рассказать о каждом более подробно, но мне кажется, что этот формат более подходящий.
Case №1: Проблема поддержки полноэкранного режима
Первый способ
В большинстве масс-маркет читов для Counter-Strike 2 существует проблема поддержки полноэкранного режима. Например, X1 утверждает, что эта проблема подвластна всем внешним читам и предлагает использовать масштабирование с помощью видеокарты.
В целом, это был первый вариант решения проблемы, но есть некоторые "но":
- Он требует специфических действий со стороны пользователя. Вы ведь не думаете, что ему интересно что-то дополнительно исправлять при покупке p2c?
- Конкретно этот способ подходит только для обладателей видеокарты от Nvidia.
Второй способ решения этой проблемы - это просто перехватить оверлей легитимной компании, которая уже исправила за вас эти проблемы =). Фактически, этот способ является стандартом индустрии и практикуется в большинстве читов (в основном в играх, защищенных EAC / BE).
Перехватить можно как минимум двумя способами:
- Выполнить так называемый “hijacking” окна игры и рисовать там свой оверлей. При этом подходе вы полностью можете контролировать атрибуты этого окна. Пожалуйста, авторизуйтесь для просмотра ссылки.
- Записывать в IPC (inter-process communication) буфер оверлея, чтобы он позже отрисовал кадр. Плюсы этого подхода в большей стабильности происходящего. Для неразбирающегося в теме человека может показаться, что задержка при использовании оверлея по сравнению с первым подходом уменьшается до минимального, и возникает полная синхронизация ESP с моделью в игре. Мы обсудим это позже. Минусы: придется реверсить(((, вы ограничены функционалом, заложенным разработчиками, структуры могут разниться от версии ПО, и нужно будет заставлять пользователей скачивать новую версию оверлея.
Злоупотребление DWM
В названии указана страшная аббревиатура DWM. Что же это такое? Desktop Window Manager (DWM) - это часть операционных систем Windows. Он отвечает за управление окнами на рабочем столе. Раньше его функция состояла только в том, чтобы обеспечить сглаживание окон и прочие визуальные эффекты, такие как Aero Glass.
С выходом Windows 8, DWM стал обязательным компонентом Windows, в тот же период времени они добавили полную поддержку глубины окон, т.е. сделали условные слои, как в фотошопе, которые позволяли полноценно накладывать одни окна на другие. Слои поделены на группы по порядку отрисовки на экране.
Вот вам enum с названиями этих групп, они, кстати, называются ZBID:
C++:
/*
from the lowest to the highest z-order:
ZBID_DESKTOP
ZBID_IMMERSIVE_BACKGROUND
ZBID_IMMERSIVE_APPCHROME
ZBID_IMMERSIVE_MOGO
ZBID_IMMERSIVE_INACTIVEMOBODY
ZBID_IMMERSIVE_NOTIFICATION
ZBID_IMMERSIVE_EDGY
ZBID_SYSTEM_TOOLS
ZBID_LOCK (Windows 10 only)
ZBID_ABOVELOCK_UX (Windows 10 only)
ZBID_IMMERSIVE_IHM
ZBID_GENUINE_WINDOWS
ZBID_UIACCESS
*/
// @author: <https://blog.adeltax.com/window-z-order-in-windows-10/>
enum ZBID
{
ZBID_DEFAULT = 0,
ZBID_DESKTOP = 1,
ZBID_UIACCESS = 2,
ZBID_IMMERSIVE_IHM = 3,
ZBID_IMMERSIVE_NOTIFICATION = 4,
ZBID_IMMERSIVE_APPCHROME = 5,
ZBID_IMMERSIVE_MOGO = 6,
ZBID_IMMERSIVE_EDGY = 7,
ZBID_IMMERSIVE_INACTIVEMOBODY = 8,
ZBID_IMMERSIVE_INACTIVEDOCK = 9,
ZBID_IMMERSIVE_ACTIVEMOBODY = 10,
ZBID_IMMERSIVE_ACTIVEDOCK = 11,
ZBID_IMMERSIVE_BACKGROUND = 12,
ZBID_IMMERSIVE_SEARCH = 13,
ZBID_GENUINE_WINDOWS = 14,
ZBID_IMMERSIVE_RESTRICTED = 15,
ZBID_SYSTEM_TOOLS = 16,
//Windows 10+
ZBID_LOCK = 17,
ZBID_ABOVELOCK_UX = 18
};
Вы могли заметить, что я к коду приложил порядок отрисовки на экране, и последним на нём рисуется ZBID_UIACCESS. Если мы создадим окно с этим атрибутом, то мы сможем просто взять и нарисовать наш оверлей поверх экрана как системный. Но как же его создать?
Вы можете воспользоваться недокументированной функцией CreateWindowInBand из user32.dll, которая подозрительно сильно похожа на CreateWindowEx, вот её прототип:
C++:
HWND WINAPI CreateWindowInBand(
DWORD dwExStyle,
LPCWSTR lpClassName,
LPCWSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam,
DWORD dwBand);
- Поддержка только Windows 8+
- Чтобы это работало в полноэкранном режиме, нужно пользоваться “оптимизацией полноэкранного режима”, что в целом логично.
В какой-то момент вы столкнетесь с проблемой: ваш чит может начать лагать у пользователей. И эти 15 миллиардов кадров, которые вы видите на своем i9-15900k 400w, ваши пользователи могут не увидеть на своем Core 2 Duo. Что же делать? Конечно, оптимизировать. Я ведь не просто так это написал в заголовке кейса. Но нам нужно ознакомиться с мемом про оптимизацию, прежде чем приступить.
Теперь можно.
Побольше swap context
У вас же есть свой драйвер, верно??? А может быть, и гипервизор?? Хипстеры. Даже если вы ответили "нет", в любом случае операция изменения памяти (независимо от того, чтение это или запись) занимает очень много процессорного времени. Но знаете что?
Если вы прочтете не 2 байта за один вызов, а 50 тысяч, то операция займет столько же времени. И вот на этом основан простой метод оптимизации. Почему бы нам не объединить чтение каждого оффсета в чтение одного большого класса? В своем чите я использовал преимущества схемы в Counter-Strike 2, написал короткий подсчет размера всех нетворк-классов и написал кодген под них.
В итоге удалось сократить количество вызовов чтения памяти с 60+ до 10-13. А что по цифрам?
До оптимизации: 1300000-1600000ns
После: 70000-110000
Разница: 1454.54% - 1857.14%
И самое главное, что при необходимости взять что-то из класса игрока количество вызовов чтения памяти не будет расти, что очень важно для большого экстернал-чита
Таймеры, часы, где же суть?
Читатель, погружаемся в лор. Представь: ты создаешь свою OC, уже сделал потоки, процессы, но появилась проблема - они все время работают и нагружают систему, даже когда не активны. Тебе нужно добавить механизм, который бы контролировал их состояние. Хорошо, придумали: в процессоре есть прерывания, будем вызывать их через каждый n-интервал и будить потоки, которые находились в спящем режиме. Но возникает проблема: что, если один поток требует одного интервала, а другой - другого? Например, чтобы включать музыку, нужно быстро обрабатывать все и минимальное время на сон, а в автозаварке кофе можно поставить больший таймер.
Если тебе пришло в голову поставить для всей системы самый низкий интервал среди приложений, поздравляю с прохождением теста - ты индус из Microsoft. Подход, казалось бы, работает, но нагружает систему, когда это не нужно.
В Windows получить текущий интервал можно с помощью утилиты clockres с System Internals:
Причем здесь вообще экстерналы и оптимизация? Чтобы процессор не нагружался и правильно ограничивать FPS, вы используете Sleep или std::this_thread::sleep_for - не имеет значения. Так вот, когда ваш поток засыпает на n ms, он может проснуться ровно по таймеру, а может и позже, ведь планировщик вызывается интервалами. Если ваш сон дольше, чем один интервал, то он будет перенесен на другой, то есть:
C++:
if( currentSleep <= 0.f)
Resume();
// else ignore. Even with 100 nanoseconds left.
// so it will actually take currentSleep + currentTImer
- Установить минимальный интервал с помощью timeBeginPeriod, но в большинстве случаев ваш сон не будет соответствовать желаемому.
- Использовать прерывания, для этого есть метод _mm_pause. Проверять, прошло ли нужное время, можно с помощью QueryPerformanceCounter, или посчитать руками заранее. При этом подходе вы будете ужасным человеком, вы будете мешать планировщику Windows.
- Вызывать NtDelayExecution с -1, чтобы спать на минимальное количество времени, но для этого нужно просчитывать нужное количество циклов для сна, ну или костыль с QueryPerformanceCounter =)
Буду честен, над этой проблемой мы в проекте провели не одну неделю, перепробовали всё: начиная со сменой движка отрисовки, заканчивая переписыванием полностью кодовой базы. В один момент, спустя месяцы, я снова решил попробовать свои силы в этой проблеме. При диалоге и рассуждении с моим хорошо знакомым seniusz, ко мне пришло озарение, что это Windows мне что-то портит. Спустя пару минут гугления обновлений Windows, я нашёл то самое! Чтобы исправить маленький FPS в оверлее, вам нужно сделать две вещи:
- Отключить Game Mode, он доступен с Windows 10 1809
- Отключить планирование графического процессора с аппаратным ускорением
К сожалению, проблема до сих пор остается частично. Что-то в Windows обрезает FPS ровно в два раза, выделяя больше ресурсов на игру. Если вы знаете, как это решить - отпишите мне, буду благодарен!
Case №4: Защищайтесь сударь
В этом кейсе нет конкретики. В большинстве внешних читов, которые мне довелось протестировать, я замечал много проблем с безопасностью от анти-чита (например, X1, Pellix, PUSSYCAT, AIMSTAR и т.д.).
Основная проблема: мало кому нужен чит, который не способен обеспечить защиту от анти-чита.
Вывод
В разработке экстернал читов есть множество проблем, но все они решаемы. Главное думать головой, и пытаться понять что делаешь.
До скорой встречи, Make cheats, not war!
Мой блог ( зачем он здесь?? ) -
Пожалуйста, авторизуйтесь для просмотра ссылки.
Источники:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Пожалуйста, авторизуйтесь для просмотра ссылки.
Пожалуйста, авторизуйтесь для просмотра ссылки.
( фраза Make cheats, not war! )