Привет друзья! В данной статье (как и во всех следующих из этого цикла, если я возьмусь за продолжение) я хочу рассмотреть один из способов обхода конкретных античитов проектов. Внимание будет уделено в особенности тем методам, которые работают на данный момент. Каждый обход античита будет подробно описан, а так же по возможности будет предоставлен реальный пример существующего чита (однако реализация его в виде скрипта предоставлена не будет).
Целью данной статьи является желание повысить осведомленность разработчиков о способах работы нынешних читов, тем самым дав им информацию о том, как это можно пофиксить, а начинающим разработчикам читов — информацию о том, как можно обходить серверные античиты.
Открывать данную цепочку будет простой в работе чит — спидхак, но и в то же время несущий в себе взрывную волну жоп от игроков.
Все мы знаем как работают современные античиты на детект данных читов — проверка рывков, максимальной скорости которую может выдать машина, но не задумывались ли вы о том, что будет если сервер попытается сам установить вам любую скорость? Правильно! Сам сервер попадет в свою же ловушку, а именно: будет большой рывок скорости от предыдущей, а так же если скорость будет установлена большая, то и превышение максимально возможной для данной машины.
Но что же делать серверу? Как понять что это он установил скорость? Для решения подобных проблем существуют задержки для античита, при которых он попросту перестает реагировать на игрока. Время бездействия на игрока определяется скриптером который писал античит, кто-то ставит огромные задержки, а кто-то все вымеряет досконально и не дает читеру и малейшей миллисекунды.
Вы скажете, что раз сервер устанавливает вам свою скорость, то соответственно он знает ее значение и ему ничего не мешает сверить ее с вашей текущей. Да, это может произойти, но ее можно будет превысить и легально, а значит, вероятнее всего, мало кто будет ставить такие проверки, ибо это чревато ложными срабатываниями.
Подведя итоги мы имеем следующее: при установке скорости сервером античит отключается на неопределенное время (обычно оно не большое, 1-3 секунды, дабы скорость могла начать падать), а значит и есть возможность подогнать наш пакетик синхры с нужной для нас скоростью 🤓
В данной статье был описан лишь один из методов обхода, а значит и за время написания данной статьи могли появиться и другие. Пробуйте, экспериментируйте и взрывайте жопы игрокам, но только там где это можно. Всем спасибо за внимание и удачи! 🤚
- mkulbakov
У вас нет ни одного мессенджера. Вы можете добавить их в своем профиле.
- 19 Февраль 2022
- #2
Можно еще было дополнить гайд тем, что некоторые системы (к примеру детект высокого пинга на забугорных серверах) берут информацию напрямую от клиента, т.е банальной подменой функции сервер уже думает что наш пинг будет равен 1
Вот пример исполнения:
local plMeta = FindMetaTable('PLAYER')
local oldFn = plMeta.Ping
local math_random = math.random
plMeta.Ping = function()
return math_random(5, 15)
end
Такую-же махинацию можно было провернуть и с консольными переменными, так как даже бинарный модуль sourcenet получает информацию о переменной с клиента (а как еще-то?) с применением функции GetConVarString.
Ну а так гайд полезный для новичков и заслуженные 10 мешков яблок «Чемпион» он получает!
- 19 Февраль 2022
- #3
Можно еще было дополнить гайд тем, что некоторые системы (к примеру детект высокого пинга на забугорных серверах) берут информацию напрямую от клиента, т.е банальной подменой функции сервер уже думает что наш пинг будет равен 1
Вот пример исполнения:
local plMeta = FindMetaTable('PLAYER') local oldFn = plMeta.Ping local math_random = math.random plMeta.Ping = function() return math_random(5, 15) end
Такую-же махинацию можно было провернуть и с консольными переменными, так как даже бинарный модуль sourcenet получает информацию о переменной с клиента (а как еще-то?) с применением функции GetConVarString.
Ну а так гайд полезный для новичков и заслуженные 10 мешков яблок «Чемпион» он получает!
Впервые о таком слышу, но это обойдется даже через cl_cmdrate +33 в консоль, да и методы эти все бесполезны когда анти чит ранится после init.lua и копирует оригинальные функции(тоесть не использует перезаписанные) и потом он дает бан проверив через jit.util.funcinfo
- mkulbakov
У вас нет ни одного мессенджера. Вы можете добавить их в своем профиле.
- 19 Февраль 2022
- #4
Впервые о таком слышу, но это обойдется даже cl_cmdrate +33, да и методы эти все бесполезны когда анти чит ранится после init.lua и копирует оригинальные функции и потом он дает бан проверив через jit.util.funcinfo
Посмотреть вложение 32466
Про пинг я проверял лично на Orbital Servers, но кодер данного австралийского проекта удалил эту проверку после того, когда я рассказал про этот обход
- mkulbakov
У вас нет ни одного мессенджера. Вы можете добавить их в своем профиле.
- 19 Февраль 2022
- #5
Впервые о таком слышу, но это обойдется даже через cl_cmdrate +33 в консоль, да и методы эти все бесполезны когда анти чит ранится после init.lua и копирует оригинальные функции(тоесть не использует перезаписанные) и потом он дает бан проверив через jit.util.funcinfo
Посмотреть вложение 32466
Русские недо-разработчики великолепных паст даже не знают что такое LuaJIT, так что про это можно забыть в русском сегменте
- 19 Февраль 2022
- #6
Русские недо-разработчики великолепных паст даже не знают что такое LuaJIT, так что про это можно забыть в русском сегменте
Всегда знал что серебро крутой
Всем привет, это тутор как написать свой обход.
Для начала,я расскажу вам основы.
Чтобы научиться писать обход,сначало нужно логнуть античит это есть тут «как логнуть античит».
После того,как вы приобрели этот туториал, получите логи по инструкции.
После того как вы получили,логи. Полетели,писать свой обход.
———————————————————————
Для начала,существуют два способа написать обход. Через «monoString» и через «void». Я бы советовал писать через «monoString», т.к код получается меньше.
Начнем с начала,нам нужно знать типы данных с++.
В обход нужно ретурнуть текст,поэтому тип данных — «string». В тип данных «string» входит «void» и «monoString», т.к «monoString» — монострока, а void — тип, спецификатор типа и ключевое слово в языках программирования С, C++, Java, C#, Objective-C, D, ActionScript и PHP. Среди современных языков программирования ключевое слово void впервые появилось в C++ для поддержки концепции обобщенных указателей.
———————————————————————
Теперь,пишем начало обхода. Я покажу как я пишу обходы.
Начало каждого хука:
monoString* (*old_safestring)(void* inst);
monoString* safestring(*void inst){
}
Далее,нам нужно писать внутренности обхода.
monoString* (*old_safestring)(void* inst);
monoString* safestring(*void inst){
std::string valtostd(old_safestring(inst)->toChars());
}
Теперь,когда мы пишем поиск чего-либо,нам нужно указать переменую valtostd.
Теперь пишем поиск.
monoString* (*old_safestring)(void* inst);
monoString* safestring(*void inst){
std::string valtostd(old_safestring(inst)->toChars());
if (contains(valtostd, «base.apk:») && contains(valtostd, «|main.»)){
}
}
Так,вот я написал поиск base.apk и |main. , чтобы вернуть оригинальные кэши игры. Которые мы получили в логи, вот пример:
base.apk:495616b01919ae6fb793dbd30df08015:94530818|main.2010.com.axlebolt.standoff2.obb:933bf54569583031ecfe5ff367c76958:1401339473
Я написал в поиск — «base.apk» и «|main.», потому-что в начале нашех хэшей указаны эти названия.
Теперь мы пишем ретурн.
monoString* (*old_safestring)(void* inst);
monoString* safestring(*void inst){
std::string valtostd(old_safestring(inst)->toChars());
if (contains(valtostd, «base.apk:») && contains(valtostd, «|main.»)){
return CreateMonoString(«base.apk:495616b01919ae6fb793dbd30df08015:94530818|main.2010.com.axlebolt.standoff2.obb:933bf54569583031ecfe5ff367c76958:1401339473»);
}
}
Все,готово. Мы ретурнули оригинальные кэши игры. Дальше нужно ретурнуть сигнатуру,сигнатура всегда одна и она не меняется. И мы тоже получаем ее в логах,вот она:
lcG7acvUIg0k4FQSQmAbyw1tN0o=
Пишем поиск сигнатуры и ретурнаем её.
monoString* (*old_safestring)(void* inst);
monoString* safestring(*void inst){
std::string valtostd(old_safestring(inst)->toChars());
if (contains(valtostd, «base.apk:») && contains(valtostd, «|main.»)){
return CreateMonoString(«base.apk:495616b01919ae6fb793dbd30df08015:94530818|main.2010.com.axlebolt.standoff2.obb:933bf54569583031ecfe5ff367c76958:1401339473»);
} else if (contains(valtostd, «=») && contains(valtostd, «data»)){
return CreateMonoString(«lcG7acvUIg0k4FQSQmAbyw1tN0o=»);
}
}
Готово,мы ретурнули сигнатуру. Теперь нужно ретурнуть проверку кэша,чтобы мы смогли в нашем апк заменить кэш без всяких проблем и играть с другим кэшем :3
monoString* (*old_safestring)(void* inst);
monoString* safestring(*void inst){
std::string valtostd(old_safestring(inst)->toChars());
if (contains(valtostd, «base.apk:») && contains(valtostd, «|main.»)){
return CreateMonoString(«base.apk:495616b01919ae6fb793dbd30df08015:94530818|main.2010.com.axlebolt.standoff2.obb:933bf54569583031ecfe5ff367c76958:1401339473»);
} else if (contains(valtostd, «=») && contains(valtostd, «data»)){
return CreateMonoString(«lcG7acvUIg0k4FQSQmAbyw1tN0o=»);
} else if (contains(valtostd, «base.apk») && contains(valtostd, «/data») && contains(valtostd, «|») && contains(valtostd, «.com.axlebolt.standoff2.obb:»)){
return CreateMonoString(«base.apk:495616b01919ae6fb793dbd30df08015:94530818»);
}
}
А теперь объязательно нужно ретурнуть анти-бан девайс,чтобы бан по девайсу снимался автоматически.
monoString* (*old_safestring)(void* inst);
monoString* safestring(*void inst){
std::string valtostd(old_safestring(inst)->toChars());
if (contains(valtostd, «base.apk:») && contains(valtostd, «|main.»)){
return CreateMonoString(«base.apk:495616b01919ae6fb793dbd30df08015:94530818|main.2010.com.axlebolt.standoff2.obb:933bf54569583031ecfe5ff367c76958:1401339473»);
} else if (contains(valtostd, «=») && contains(valtostd, «data»)){
return CreateMonoString(«lcG7acvUIg0k4FQSQmAbyw1tN0o=»);
} else if (contains(valtostd, «base.apk») && contains(valtostd, «/data») && contains(valtostd, «|») && contains(valtostd, «.com.axlebolt.standoff2.obb:»)){
return CreateMonoString(«base.apk:495616b01919ae6fb793dbd30df08015:94530818»);
} else if (contains(valtostd, «3») && contains(valtostd, «|») && contains(valtostd, » «) && contains(valtostd.length() == 16){
return CreateMonoString(gen_random(16).c_str());
}
}
Готово. Теперь нужно сделать так чтобы ваша либка не детектилась, но этому вы научитесь когда купите следущий урок!
А теперь,нужно закончить наш код обхода!
monoString* (*old_safestring)(void* inst);
monoString* safestring(*void inst){
std::string valtostd(old_safestring(inst)->toChars());
if (contains(valtostd, «base.apk:») && contains(valtostd, «|main.»)){
return CreateMonoString(«base.apk:495616b01919ae6fb793dbd30df08015:94530818|main.2010.com.axlebolt.standoff2.obb:933bf54569583031ecfe5ff367c76958:1401339473»);
} else if (contains(valtostd, «=») && contains(valtostd, «data»)){
return CreateMonoString(«lcG7acvUIg0k4FQSQmAbyw1tN0o=»);
} else if (contains(valtostd, «base.apk») && contains(valtostd, «/data») && contains(valtostd, «|») && contains(valtostd, «.com.axlebolt.standoff2.obb:»)){
return CreateMonoString(«base.apk:495616b01919ae6fb793dbd30df08015:94530818»);
} else if (contains(valtostd, «3») && contains(valtostd, «|») && contains(valtostd, » «) && contains(valtostd.length() == 16){
return CreateMonoString(gen_random(16).c_str());
} else if (contains(valtostd, «/data/») && contains(valtostd, «.so») && contains(valtostd, «|»)){
}
return old_safestring(inst);
}
}
———————————————————————
[туториал BY TW1X TEAM ( noad )]
Доброго всем вечера. Хотелось бы с вами поделиться небольшим сливом для обхода античита EAC. Многие из вас наверняка знают этот баг, а некоторые нет. Поэтому эта статья как раз для некоторых. Сразу оговорюсь, это неполноценный обход. Он контуженный, на костылях и потребуются небольшие танцы с бубном. Короче в чём прикол:
В любой игре, которая поддерживается античитом EAC, есть папка: EasyAntiCheat. В качестве примера возьму игру Bloodhunt. У неё эта папка располагается по этому пути — D:SteamLibrarysteamappscommonBloodhuntEasyAntiCheat
Ну так вот. В этой папке есть файл — Settings.json. Он может находиться как в этой папке, так и в подпапке — Launcher. У каждой игры по разному. Вся суть вот в чём. Сначала мы устанавливаем игру, устанавливаем из неё easy anti cheat (EAC). Заходим в игру с запущенным античитом. После этого выходим. Открываем файл Settings.json через Notepad++ и редактируем следующие пункты: 1)»executable» 2)»productid» 3)»sandboxid» Ниже приведены скрины для игры BattleBit Remastered Playtest.
ОРИГИНАЛ:
ИЗМЕНЁННЫЙ ВАРИАНТ:
После этого сохраняем файл и заходим в игру. Воля. Некоторые игры пропустят вас в матч с игроками, а некоторые пустят только до лобби или до тренировочной комнаты. Всё зависит от игры и от того, какая версия EAC на ней накатана. В общем пробуйте, экспериментируйте. Если найдёте в какой-нибудь игре лазейку, обязательно поделитесь со всеми братанами. )) Я например подобным методом целый месяц играл без античита в игре Squad и свободно редактировал память игры в Cheat Engine.
Если в вашей игре подобный метод не работает, можно конечно упороться и перепробовать танцы с бубном. Например вот две упоротых инструкции:
1)Удалил античит, введя в командной строке: sc delete EasyAntiCheat, потом перешел в папку с игрой Realm Royale, нажал на ярлык античита, удалил, потом установил для этой игры заново античит, финиш. После этого установил анчичит, но уже с игры BattleBit Remastered, потом перешел в сеттинг файл игры Realm Royale и поставил в этом файле 69. После этого запустил игру Realm Royale с ярлыка на рабочем столе. Работает.
2)Удалил полностью античит EAC с компа, удалил все его папки, подпапки и файлы, а именно — AppDataRoamingEasyAntiCheat и C:Program Files (x86)EasyAntiCheat_EOS. Также не забудь удалить службу EAC и его драйвер через программу Autorun Organizer. Выключил комп. Включил. Запустил стим, удалил игру BattleBit Remastered с компа. Потом установил её опять и запустил с окна стим, выбрал install Easyanticheat. После этого запустил игру с ярлыка на рабочем столе. Игра запустилась с античитом, я выхожу. Закрываю стим. Меняю сеттинг файл, но приписываю в него уже новые цифры 1 2 3, сохраняю. Запускаю стим. Запускаю игру battlebit но уже с рабочего стола ярлыка. Игра работает без античита. Выхожу. Потом устанавливаю новую тоже с античитом EAC, например игру Squad. Как установилась, выхожу и закрываю стим. Беру этот сеттинг файл с папки игры BattleBit Remastered и копирую его в папку игры Squad, открываю его и вписываю в него «executable»: и «title»: с названием ярлыка Squad игры, сохраняю этот файл. Запускаю стим. Запускаю игру Squad с ярлыка на рабочем столе. Игра работает без античита, но не всегда.
Загрузка…
Доброго всем вечера. Хотелось бы с вами поделиться небольшим сливом для обхода античита EAC. Многие из вас наверняка знают этот баг, а некоторые нет. Поэтому эта статья как раз для некоторых. Сразу оговорюсь, это неполноценный обход. Он контуженный, на костылях и потребуются небольшие танцы с бубном. Короче в чём прикол:
В любой игре, которая поддерживается античитом EAC, есть папка: EasyAntiCheat. В качестве примера возьму игру Bloodhunt. У неё эта папка располагается по этому пути — D:SteamLibrarysteamappscommonBloodhuntEasyAntiCheat
Ну так вот. В этой папке есть файл — Settings.json. Он может находиться как в этой папке, так и в подпапке — Launcher. У каждой игры по разному. Вся суть вот в чём. Сначала мы устанавливаем игру, устанавливаем из неё easy anti cheat (EAC). Заходим в игру с запущенным античитом. После этого выходим. Открываем файл Settings.json через Notepad++ и редактируем следующие пункты: 1)»executable» 2)»productid» 3)»sandboxid» Ниже приведены скрины для игры BattleBit Remastered Playtest.
ОРИГИНАЛ:
ИЗМЕНЁННЫЙ ВАРИАНТ:
После этого сохраняем файл и заходим в игру. Воля. Некоторые игры пропустят вас в матч с игроками, а некоторые пустят только до лобби или до тренировочной комнаты. Всё зависит от игры и от того, какая версия EAC на ней накатана. В общем пробуйте, экспериментируйте. Если найдёте в какой-нибудь игре лазейку, обязательно поделитесь со всеми братанами. )) Я например подобным методом целый месяц играл без античита в игре Squad и свободно редактировал память игры в Cheat Engine.
Если в вашей игре подобный метод не работает, можно конечно упороться и перепробовать танцы с бубном. Например вот две упоротых инструкции:
1)Удалил античит, введя в командной строке: sc delete EasyAntiCheat, потом перешел в папку с игрой Realm Royale, нажал на ярлык античита, удалил, потом установил для этой игры заново античит, финиш. После этого установил анчичит, но уже с игры BattleBit Remastered, потом перешел в сеттинг файл игры Realm Royale и поставил в этом файле 69. После этого запустил игру Realm Royale с ярлыка на рабочем столе. Работает.
2)Удалил полностью античит EAC с компа, удалил все его папки, подпапки и файлы, а именно — AppDataRoamingEasyAntiCheat и C:Program Files (x86)EasyAntiCheat_EOS. Также не забудь удалить службу EAC и его драйвер через программу Autorun Organizer. Выключил комп. Включил. Запустил стим, удалил игру BattleBit Remastered с компа. Потом установил её опять и запустил с окна стим, выбрал install Easyanticheat. После этого запустил игру с ярлыка на рабочем столе. Игра запустилась с античитом, я выхожу. Закрываю стим. Меняю сеттинг файл, но приписываю в него уже новые цифры 1 2 3, сохраняю. Запускаю стим. Запускаю игру battlebit но уже с рабочего стола ярлыка. Игра работает без античита. Выхожу. Потом устанавливаю новую тоже с античитом EAC, например игру Squad. Как установилась, выхожу и закрываю стим. Беру этот сеттинг файл с папки игры BattleBit Remastered и копирую его в папку игры Squad, открываю его и вписываю в него «executable»: и «title»: с названием ярлыка Squad игры, сохраняю этот файл. Запускаю стим. Запускаю игру Squad с ярлыка на рабочем столе. Игра работает без античита, но не всегда.
Лишаем зрения античит систему многопользовательских игр
Встроенной системой слежения за процессом и окружающей его средой, с целью противодействия различным неавторизированным модификациям кода, уже никого не удивить: практически любой мало-мальски популярный многопользовательский игровой проект имеет нечто подобное. В этой статье мы проанализируем используемую разработчиками из Blizzard клиентскую защиту, а также реализуем один из эффективных способов ее обхода.
WARNING
Автор и редакция напоминают, что вся информация опубликована исключительно в образовательных целях, описанные в статье действия могут противоречить лицензионному соглашению Blizzard Entertaiment.
Warden (переводится с английского как смотритель, надзиратель) — именно так решили назвать защитную систему разработчики популярнейших в своих жанрах игр из Blizzard. Система, являясь фактически частью Battle.net, используется в таких проектах, как World of Warcraft, StarCraft II и Diablo 3. Только лишь по официальным данным за все время были забанены десятки тысяч аккаунтов Battle.net, и немалая часть при этом — заслуга Warden.
Безмолвный смотритель
Для начала, пожалуй, стоит выяснить, что собой представляет Warden. Система состоит из двух частей: серверной и клиентской, и, само собой, мы будем иметь дело только с клиентской частью. Как уже было сказано ранее, Warden не является неотъемлемой частью игрового кода. Код клиентской части подгружается динамически с Battle.net в виде образов, отдаленно напоминающих по своей структуре Portable Executable, которые затем отображаются по случайным адресам в адресном пространстве игрового процесса. Стоит также отметить, что большая часть кода клиентской части Warden обфусцирована и может изменяться от одной игровой сессии к другой.
Warden представляет собой пассивный механизм защиты, и все, чем занимается клиентская часть Warden, — это сбор информации, которая впоследствии отправляется серверной части. В целом примерный алгоритм работы клиентской части Warden выглядит следующим образом:
- Получение списка относительных адресов для сканирования.
- Считывание необходимого количества байт по каждому из адресов.
- Расчет хешей.
- Компоновка пакета с хешами и отправка его на сервер.
Процедура повторяется с некоторой периодичностью несколько раз в минуту. Если серверная часть обнаруживает несоответствие хешей эталонным значениям, считается, что используются запрещенные модификации кода. Каких-либо незамедлительных действий при этом не предпринимается — аккаунт просто помечается как нарушающий правила, а о том, что ты «попался» можно будет узнать лишь через некоторое время, когда учетная запись уже будет заблокирована. Целиком аккаунт Battle.net (который может содержать множество прикрепленных лицензий) при этом не блокируется — только учетная запись игры.
Заблокированная игровая учетная запись
Другие статьи в выпуске:
Хакер #187. Обходим Blizzard Warden
Против системы
Нейтрализовать Warden, просто отключив его либо заблокировав его работу, не удастся: система устроена таким образом, что серверная часть в любом случае должна получать от клиентской ответные пакеты, которые, в свою очередь, должны содержать информацию о сканировании. Следовательно, выход только один — не попадаться. Добиться этого можно как минимум тремя способами:
- Обходить стороной заведомо опасные адреса при внесении модификаций в код.
- Использовать косвенное внедрение, перехватывая один из методов DirectX — Device.EndScene().
- Прятать все совершенные модификации на лету (при сканировании).
Первый вариант будет работать до поры до времени и по большому счету обходом как таковым не является. Второй вариант (перехват EndScene() ) действительно неплохо работает, функция вызывается после завершения построения каждого выводимого на экран кадра и перехватывается, например вполне легальными программами видеозахвата, что не дает Warden возможности однозначно трактовать изменения в коде функции как запрещенные модификации. Тем не менее вариант больше годится для ботов и успешно ими эксплуатируется на протяжении уже нескольких лет. Третий вариант идеально подходит для статичных модификаций (как, например, включение отрисовки всей карты в Star Craft — maphack), кроме того, его реализация сама по себе интереснее и технологичнее. Именно последний вариант подробно и рассмотрим далее.
Вопреки всеобщему заблуждению, никакой утечки личной информации (в версии на момент написания статьи) не происходит: сканируются лишь некоторые участки адресного пространства игрового процесса.
Очевидно, что для сокрытия произведенных модификаций необходимо внедриться в сканирующий код Warden. Как известно, этот код не присутствует в процессе со старта, к тому же при загрузке получает случайный адрес. На первый раз его можно обнаружить при помощи отладчика, просто установив breakpoint на чтение любого из сканируемых адресов (от какого-либо старого, давно детектируемого хака). Например, для последнего (на момент написания статьи) билда World of Warcraft, установив breakpoint по относительному базе основного образа адресу 0x0045A6F0 , мы попадаем в следующий участок кода:
Сердце сканера Warden
Опытным путем было установлено, что обнаруженный код не подвергается полиморфным изменениям, в отличие от всего остального модуля, к тому же изменялся он за последние годы лишь однажды, что делает его идеальной мишенью для внедрения. Но так как этот код — часть загружаемого динамически модуля, то необходимо будет также перехватить момент его появления в процессе, чтобы внести изменения до первого исполнения. В случае с WoW загрузчик является частью кода игры и находится прямо в Wow.exe (для 32-битной версии), его можно найти, перелопатив километры листингов в дизассемблере, или пойти более хитрым путем. Память под загружаемые образы модулей Warden выделяется функцией VirtualAlloc() , лог вызовов, с указанием места, откуда был произведен вызов, будет содержать адрес, принадлежащий загрузчику.
При этом нет нужды перебирать все записи: после логина и входа на игровой реалм необходимый модуль Warden будет уже загружен, можно просто произвести поиск по всему адресному пространству процесса бинарного паттерна, соответствующего ранее найденной процедуре сканирования данных:
Поиск загрузчика
Таким образом мы определим точное текущее местоположение необходимого нам кода Warden, а лог вызовов VirtualAlloc() позволит определить, откуда именно была запрошена память под этот код, указав тем самым на загрузчик модулей Warden. Проанализировав в дизассемблере код загрузчика, можно найти подходящее место для перехвата. Для этого нужно найти подходящий момент, когда все секции образа, полученного из Сети, будут успешно отображены в АП процесса, после этого можно будет внедрять перехват, модифицирующий код Warden. Подходящим участком может быть вызов VirtualProtect() , устанавливающий финальные права доступа к секциям:
Загрузчик модулей Warden
Код функции-трамплина, переход на которую установлен вместо call ds:VirtualProtect , может выглядеть следующим образом:
Поиск данных по бинарному паттерну
Чтобы делать модифицирующие код патчи не зависящими от версии игр и не перебивать раз за разом смещения, потребуется возможность поиска по двоичному паттерну (шаблону). В этом случае на основе кода, требующего изменения, создается паттерн, содержащий достаточно информации для того, чтобы при совпадении можно было уверенно сказать, что нашлось именно то, что требовалось. Существует масса различных возможностей реализации поиска по шаблону. В предлагаемом мной решении поиск производится по шаблону вида: xA?B (где A и B — натуральные числа, x — точное совпадение байт, количество которых указано следующими символами, ? — пропускаемые байты).
Полные исходники можно посмотреть в прилагаемом проекте.
Патчер
Для того чтобы была возможность скрывать свои действия от глаз Warden, необходимо запоминать абсолютно все производимые в памяти процесса изменения и иметь доступ к оригинальным данным, существовавшим до внесения изменений. Любые изменения (перехваты, подмены и прочее) должны производиться одним и тем же средством, которое должно гарантировать выполнение изложенных требований:
Список структур, содержащий информацию по всем совершенным в процессе изменениям, может принадлежать объекту с глобальной областью видимости. Модифицирование кода теперь может производиться примерно следующим образом:
Использование централизованного патчера не доставляет дополнительных хлопот, при этом помимо простого доступа к оригинальным данным мы получаем возможность откатить любое изменение, вернув все к первоначальному состоянию, что иногда бывает весьма полезно.
Если тебя заинтересовала представленная в статье тематика и ты хочешь покопать еще глубже, то могу порекомендовать, возможно, лучший специализированный форум.
Невидящее око Warden’а
Теперь, когда есть вся необходимая информация и инструменты, осталось подменить сканирующую процедуру Warden своей, которая вместо модифицированных данных будет подставлять оригинальные. Хеши в таком случае будут идентичны тем, что хранятся на сервере, и изменения кода останутся незамеченными.
Чтобы в поле зрения Warden не попало ни одного измененного байта, при каждом вызове сканирования необходимо искать пересечение множеств сканируемых адресов с адресами пропатченных данных. Так как патчи, скорее всего, не будут идти один за другим (это не имеет смысла) — будет максимум одно пересечение для одного скана и данные можно будет брать из структуры, связанной с каким-то одним конкретным патчем. Все возможные варианты пересечений сводятся к одному двойному условию: либо адрес начала множества сканируемых байт входит во множество адресов патча, либо наоборот. Таким образом, мы должны перебирать все патчи, проверяя заданное условие:
Получив сопряженную с текущим сканированием структуру с информацией о патче, подменить данные не составит труда:
Используя приведенный код вместо оригинальной процедуры сканирования, мы можем контролировать активность Warden, не давая ему возможности обнаружить любые внесенные в код изменения, даже в том случае, если Warden попытается проверить на целостность самого себя.
Лог активности Warden
SOURCE
В статье приведен облегченный и неполный исходный код, автор создал полноценный рабочий проект, прилагаемый к статье. Не поленись и загляни в исходники, вполне возможно, что ты найдешь там нечто полезное для себя.
Proof of concept
В качестве демонстрации работоспособности обхода с извлечением какой-то практической пользы было принято решение произвести модификацию кода World of Warcraft по относительному смещению 0x008C9A3E , которое проверяется сканером Warden. Процедура, соответствующая этому смещению, ответственна за проверку прав на исполнение Lua-скрипта (многие из функций WoW API заблокированы для пользователя и могут быть использованы только родным пользовательским интерфейсом). Участок кода в области этого смещения выглядит следующим образом:
Само смещение соответствует условному переходу после сравнения глобальной переменной, содержащей идентификатор уровня доступа для текущего контекста, с нолем (ноль соответствует самым высоким правам). Заменив условный переход безусловным, получаем возможность использовать любые функции WoW API, создавая сложные и «умные» скрипты, автоматизирующие многие игровые действия (самый примитивный пример использования: забиндить всю ротацию спеллов на одну кнопку, с проверкой кулдаунов и так далее, что сделать изначально невозможно). Упрощенный код установки патча выглядит примерно так:
После установки патча становятся доступны прямо из макросов «защищенные» функции WoW API, а в логе активности Warden мы можем наблюдать предотвращенные попытки просканировать пропатченную область. Убедиться в этом ты можешь, скомпилировав и опробовав прилагаемые к статье исходники.
Как насчет остальных проектов Blizzard?
В статье был рассмотрен вариант перехвата кода загрузчика для WoW, у других проектов этот код находится в обфусцированной библиотеке battle.net.dll, по которой в принципе невозможно создать не зависящий от версии библиотеки паттерн для поиска кода загрузчика. В этом случае, как один из вариантов, можно перехватывать все вызовы VirtualProtect(), совершенные из battle.net.dll, обрабатывая их примерно следующим образом:
Полная свобода действий
Возможность безнаказанно вносить любые изменения в игровой клиент открывает широчайшие перспективы для дальнейших исследований. На самом деле в играх Blizzard можно сотворить абсолютно все, что только можно себе представить или захотеть. Об одних лишь возможностях разблокированных скриптов Lua в WoW можно было бы написать отдельную статью. Ведь даже простые скрипты могут избавить игрока от рутинных действий или снизить зависимость от реакции и внимательности, позволив уделять чуть больше времени другим вещам. При этом возможности свободных модификаций клиента не ограничиваются простой разблокировкой тех или иных возможностей. В общем, дерзай!
Обход EAC (EasyAntiCheat) 2022 для любых игр / EAC bypass for Cheat Engine
Доброго всем дня. Давно руки не доходили и наконец дошли. Всё больше и больше игр начали накрывать EAC-ом, так что пришлось запилить обход для этого античита, который, к слову, я всегда считал Эверестом.
Обход реализовал в двух исполнениях:
1. Один для Cheat Engine, чтобы можно было спокойно искать, отсеивать, редактировать значения в памяти прям при запущенном EasyAntiCheat. На примере стимовской BeastsOfBermuda:
А также свежий скриншот (15.02.2022) из игры LostArk (тоже Steam):
Ну вот по сути он же, только модернизированный.
Обратите внимание, что обход даёт только доступ к памяти (просмотру, редактированию, отсеиванию и тд). Возможность аттачить дебаггер, юзать встроенный в СЕ спидхак и прочие фишки он использовать не позволяет — он строго только для работы с памятью игры! Нашёл скорость персонажа, отсеял, увеличил значение и профит — вот это, например, работает, ну проще говоря)
2. Второй для разработчиков читов: просто даёт вам интерфейс, с помощью которого вы в своих читах программно можете получать полный доступ к памяти игры и дальше уже творить чё хотите.
В данный момент не палится, игра не вылетает, не дисконнектит. Работает на последней версии EAC, предполагаю, во всех играх. Протестирован на Windows 10 x64.
И главное: Это не инструмент для получения голды, это не оконнка, это не снятие EasyAntiCheat, это не обход антикликеров, это именно обход для CE и для доступа к памяти игры. Сразу предупреждаю, чтобы не было лишних вопросов.
Поменяю на что-нибудь читерской тематики или продам.
Изменения в популярном античите BattlEye и способы их обхода
Время идёт, античиты меняются, и для повышения эффективности продукта в них появляются и исчезают функции. Год назад я подготовил подробное описание шелл-кода BattlEye в своём блоге [перевод на Хабре], и эта часть статьи станет простым отражением изменений, внесённых в шелл-код.
Чёрный список временных меток
В последнем анализе BattlEye, в списке теневого бана было всего две метки дат времени компиляции, и похоже, что разработчики решили добавить гораздо больше:
0x5B12C900 (action_x64.dll)
0x5A180C35 (TerSafe.dll, Epic Games)
0xFC9B9325 (?)
0x456CED13 (d3dx9_32.dll)
0x46495AD9 (d3dx9_34.dll)
0x47CDEE2B (d3dx9_32.dll)
0x469FF22E (d3dx9_35.dll)
0x48EC3AD7 (D3DCompiler_40.dll)
0x5A8E6020 (?)
0x55C85371 (d3dx9_32.dll)
0x456CED13 (?)
0x46495AD9 (D3DCompiler_40.dll)
0x47CDEE2B (D3DX9_37.dll)
0x469FF22E (?)
0x48EC3AD7 (?)
0xFC9B9325 (?)
0x5A8E6020 (?)
0x55C85371 (?)
Мне не удалось идентифицировать оставшиеся временные метки, а два 0xF******* — это хеши, созданные детерминированными сборками Visual Studio. Благодарю @mottikraus и T0B1 за идентификацию некоторых временных меток.
Проверки модулей
Как показал основной анализ, ключевой особенностью BattlEye является перебор модулей, и с момента прошлого анализа в список был добавлен ещё один модуль:
Вероятно, это обнаружение определённых прокси-dll, так как здесь проверяется размер таблицы переадресации.
Заголовки окон
В предыдущем анализе при помощи названий окон помечались флагами различные поставщики читов, но с тех пор шелл-код перестал проверять эти заголовки окон. Список заголовков окон был полностью заменён на:
Названия образов
BattlEye печально известен тем, что использует очень примитивные методы обнаружения, и одним из них является чёрный список названий образов. С каждым годом список забаненных названий образов становится всё длиннее, а за последние 11 месяцев были добавлены пять новых:
frAQBc8W.dll
C:\Windows\mscorlib.ni.dll
DxtoryMM_x64.dll
Project1.dll
OWClient.dll
Стоит заметить, что присутствие модуля с названием, соответствующим любому из пунктов списка, не будет означать, что вас сразу же забанят. Механизм создания отчётов также передаёт базовую информацию о модуле, которая скорее всего используется для того, чтобы отличить читы от коллизий на сервере BattlEye.
7-Zip широко использовался и продолжает использоваться участниками чит-сцены как заполнитель памяти для пустот кода (code-caves). BattlEye пытается бороться с этим, выполняя очень плохую проверку целостности, которую со времени моей предыдущей статьи изменили:
Похоже, разработчики BattlEye догадались, что моя предыдущая статья привела к тому, что многие пользователи обходят эту проверку, просто копируя нужные байты в место, проверяемое BattlEye. Как же они исправили ситуацию? Сместили проверку на восемь байтов и продолжили использовать тот же плохой способ проверки целостности. Исполняемый раздел read-only, и всё, что вам нужно сделать — загрузить 7-Zip с диска и сравнить перемещённые разделы друг с другом; если есть какие-то расхождения, то что-то не так. Серьёзно, ребята, выполнять проверки целостности не так сложно.
Проверка сети
Перебор таблицы TCP по-прежнему работает, но после того, как я выпустил предыдущий анализ, критикующий разработчиков за пометку флагами IP-адресов Cloudflare, они всё-таки убрали эту проверку. Античит всё равно сообщает о порте, который использует для соединения xera.ph, но разработчики добавили новую проверку, чтобы определять, есть ли у процесса с соединением активная защита (предположительно, это выполняется при помощи обработчика).
Благодарю IChooseYou и abstract
Обход по стеку BattlEye
Взлом игр — постоянная игра в кошки-мышки, поэтому слухи о новых приёмах распространяются как пожар. В этой части мы рассмотрим новые эвристические техники, которые недавно добавил в свой арсенал крупный поставщик античитов BattlEye. Чаще всего эти техники называют обходом по стеку (stack walking). Обычно они реализуются обработкой функции и проходом по стеку, чтобы выяснить, кто же конкретно вызвал эту функцию. Зачем это нужно делать? Как и любая другая программа, хаки видеоигр имеют набор хорошо известных функций, которые они используют для получения информации от клавиатуры, вывода в консоль или вычисления определённых математических выражений. Кроме того, хаки видеоигр любят скрывать своё существование, будь то в памяти или на диске, чтобы античитерское ПО их не нашло. Но что забывают читерские программы, так это то, что регулярно вызывают функции из других библиотек, и это можно использовать для эвристического обнаружения неизвестных читов. Реализовав движок обхода по стеку для таких функций, как std::print , мы сможем найти эти читы, даже если они маскируются.
BattlEye реализовал «обход по стеку», несмотря на то, что публично об этом не заявлялось и на момент выпуска статьи оставалось только слухами. Обратите внимание на кавычки — то, что вы здесь увидите, на самом деле не настоящий обход по стеку, а просто сочетание проверки обратного адреса и дампа вызывающей программы. Настоящая реализация обхода по стеку проходила бы по стеку и генерировала настоящий стек вызовов.
Как я объяснял в предыдущей статье про BattlEye, система античита динамически выполняет потоковую передачу шелл-кода в процесс игры, когда она запущена. Эти шелл-коды имеют разные размеры и задачи, и не передаются одновременно. Замечательное свойство подобной системы заключается в том, что исследователям требуется динаические анализировать античит, в процессе мультиплеерного матча, что усложняет определение характеристик этого античита. Также это позволяет античиту применять к разным пользователям различные меры, например, передавать более глубоко инвазивный модуль только тому человеку, который имеет необычно высокое соотношение убийств и смертей, и тому подобное.
Один из таких шелл-кодов BattlEye отвечает за выполнение этого анализа стека; мы будем называет его shellcode8kb, потому что он немного меньше по сравнению с shellcodemain, который я задокументировал здесь. Этот небольшой шелл-код при помощи функции AddVectoredExceptionHandler подготавливает векторизированный обработчик исключений, а затем устанавливает ловушки прерываний на следующих функциях:
GetAsyncKeyState
GetCursorPos
IsBadReadPtr
NtUserGetAsyncKeyState
GetForegroundWindow
CallWindowProcW
NtUserPeekMessage
NtSetEvent
sqrtf
__stdio_common_vsprintf_s
CDXGIFactory::TakeLock
TppTimerpExecuteCallback
Для этого он просто итеративно обходит список стандартно используемых функций, присваивая первую инструкцию соответствующей функции значение int3, которое используется как точка останова. После установки точки останова все вызовы соответствующей функции проходят через обработчик исключений, имеющий полный доступ к регистрам и стеку. Имея этот доступ, обработчик исключений создаёт дамп адреса вызывающей программы из вершины стека, и в случае выполнения одного из эвристических условий 32 байта вызывающей функции дампятся и отправляются на сервера BattlEye с идентификатором отчёта 0x31:
Как мы видим, обработчик исключений выполняет дамп всех вызывающих функций в случае бесцеремонного изменения страницы памяти или когда функция не принадлежит к известному модулю процесса (тип страницы памяти MEM_IMAGE не задан manualmapper-ами). Также он выполняет дамп вызывающих функций, когда не удаётся вызвать NtQueryVirtualMemory, чтобы читы не привязывались к этому системному вызову и не скрывали свой модуль от дампера стека. Последнее условие на самом деле довольно интересное, оно помечает все вызывающие функции, использующие гаджет jmp qword ptr [rbx] — способ, применяемый для «спуфинга обратного адреса». Он выпущен моим коллегой-участником тайного клуба с ником namazso. Похоже, разработчики BattlEye увидели, что люди пользуются этим способом спуфинга в их играх и решили нацелиться непосредственно на него. Здесь стоит упомянуть, что описанный namazsos способ работает хорошо, достаточно просто использовать другой гаджет, или полностью отличающийся, или просто другой регистр — это не важно.
Совет разработчикам BattlEye: используемый вами для поиска CDXGIFactory::TakeLock в памяти неверен, потому что вы (случайно или намеренно) включили CC padding, который сильно отличается при каждой компиляции. Для максимальной совместимости нужно убрать padding (первый байт в сигнатуре) и так вы скорее всего поймаете больше читеров 🙂
Полная структура, отправляемая серверу BattlEye, выглядит так:
Распознавание гипервизора в BattlEye
Игра в кошки-мышки в области взлома игр продолжает оставаться источником новаций в эксплойтах и борьбе с читами. Использование технологии виртуализации во взломе игр начало активно развиваться после появления таких простых в применении гипервизоров, как DdiMon Сатоси Танда и hvpp Петра Бенеша. Эти два проекта используются большинством платных читов андерграундной хакерской сцены благодаря низкому порогу вхождения и подробной документации. Эти релизы с большой вероятностью ускорили гонку вооружений в области гипервизоров, которая сейчас начинает проявляться в сообществе хакеров игр. Вот что об этой ситуации говорит администратор одного из крупнейших сообществ взлома игр под ником wlan:
Широкое распространение гипервизоров объясняется недавними усовершенствованиями в античитах, которые оставили хакерам очень мало возможностей для модификации игр традиционными способами. Популярность гипервизоров можно объяснить простотой избегания античита, потому что виртуализация упрощает сокрытие информации при помощи таких механизмов, как syscall hooks и MMU virtualization.
Недавно в BattlEye было реализовано распознавание распространённых гипервизоров наподобие упомянутых выше платформ (DdiMon, hvpp) при помощи обнаружения на основе времени. Это распознавание пытается обнаружить нестандартные значения времени инструкции CPUID. CPUID — это относительно малозатратная на реальном оборудовании инструкция, обычно требующая всего двух сотен циклов, а в виртуальном окружении её выполнение может занимать в десять раз больше времени из-за лишних операций, вызываемых движком интроспекции. Движок интроспекции непохож на реальное оборудование, которое просто выполняет операцию ожидаемым образом, поскольку он на основании произвольного критерия отслеживает и условно изменяет данные, возвращаемые гостю.
Забавный факт: CPUID активно используется в этих процедурах временнОго распознавания, потому что это инструкция с безусловным выходом, а также инструкция с непривилегированной сериализацией. Это значит, что CPUID используется в качестве барьера и гарантирует, что инструкции до и после неё будут выполнены; тайминги при этом становятся независимыми от обычного переупорядочивания инструкций. Можно также использовать инструкции наподобие XSETBV, тоже выполняющих безусловный выход, но для обеспечения независимого тайминга для этого потребуется какая-нибудь барьерная инструкция, чтобы до или после неё не произошло никакого переупорядочивания, влияющего на надёжность таймингов.
Распознавание
Ниже представлена процедура распознавания из модуля BattlEye «BEClient2»; я выполнил её реверс-инжиниринг и воссоздал код на псевдо-C, а потом опубликовал его в twitter. Спустя день после моего твита разработчики BattlEye неожиданно изменили обфускацию BEClient2, видимо надеясь, что это помешает мне анализировать модуль. Предыдущая обфускация не менялась больше года, но изменилась на следующий день после моего твита о ней — впечатляющая скорость.
Как я говорил выше, это самая распространённая техника распознавания с использованием безусловно перехватываемых инструкций. Однако она уязвима перед подделкой времени, и об этом мы подробно расскажем в следующем разделе.
Обход распознавания
У такого способа распознавания есть проблемы. Во-первых, он подвержен подделке времени, которая обычно выполняется двумя способами: смещением TSC в VMCS или уменьшением TSC при каждом выполнении CPUID. Существует много других способов справиться с атаками на основе времени, но последний гораздо проще в реализации, потому что можно гарантировать, что время выполнения инструкции будет находиться в пределах одного-двух тактов синхронизации выполнения на реальном оборудовании. Сложность обнаружения этой техники подделки времени зависит от опыта разработчика. В следующем разделе мы рассмотрим распознавание подделки времени и улучшение реализации, созданной в BattlEye. Второй причиной изъяна этого способа распознавания является то, что задержка CPUID (время выполнения) в разных процессорах сильно отличается и в зависимости от значения листа. На выполнение может потребоваться время в пределах 70-300 тактов. Третья проблема этой процедуры распознавания заключается в использовании SetThreadPriority. Эта функция Windows используется для задания значения приоритета заданного дескриптора потока, однако ОС не всегда слушает запрос. Эта функция является просто предложением повышения приоритета потока, и нет гарантии, что оно произойдёт. Таким образом, появляется возможность того, что этот способ будет подвержен воздействию прерываний или других процессов.
Обойти распознавание в этом случае легко, и описанная техника подделки времени эффективно побеждает этот способ распознавания. Если разработчики BattlEye захотят улучшить этот способ, то в следующем разделе приведены некоторые рекомендации.
Усовершенствование
Эту функцию можно улучшить множеством способов. Во-первых, можно намеренно отключить прерывания и принудительно задать приоритет потока, изменив CR8 на самый высокий уровень IRQL. Также было бы идеально изолировать эту проверку в одном ядре ЦП. Ещё одно улучшение: следует использовать разные таймеры, однако многие из них не так точны, как TSC, но существует один такой таймер под названием таймер APERF, или Actual Performance Clock. Я рекомендую этот таймер, потому что с ним сложнее жульничать и он только накапливает счётчик, когда логический процессор находится в состоянии питания C0. Это великолепная альтернатива использованию TSC. Также можно использовать таймер ACPI, HPET, PIT, таймер GPU, таймер NTP или таймер PPERF, который похож на APERF, но считает такты, которые воспринимаются как выполнение инструкций. Недостаток этого заключается в том, что необходимо включение HWP, который может быть отключен промежуточным оператором, а потому оказывается бесполезным.
Ниже представлена улучшенная версия процедуры распознавания, которая должна выполняться в ядре:
Примечание: IET означает Instruction Execution Time (время выполнения инструкции).
Тем не менее, процедура всё равно может быть очень ненадёжной в обнаружении распространённых гипервизоров, поскольку время выполнения CPUID может очень сильно варьироваться. Лучше было бы сравнивать IET двух инструкций. Одна из них должна иметь большую задержку выполнения, чем CPUID. Например, это может быть FYL2XP1 — арифметическая инструкция, выполнение которой занимает чуть больше времени, чем среднее IET инструкции CPUID. Кроме того, она не вызывает никаких ловушек в гипервизоре и её время можно надёжно замерить. При помощи этих двух функций функция профилирования могла бы создавать массив для хранения IET инструкций CPUID и FYL2XP1. При помощи таймера APERF можно было бы получать начальный такт арифметической инструкции, выполнять инструкцию и вычислять для неё дельту тактов. Результаты можно было бы сохранять в массив IET в течение N циклов профилирования, получая среднее значение, и повторять процесс для CPUID. Если время выполнения инструкции CPUID больше, чем у арифметической инструкции, то это надёжный признак того, что система виртуальна, потому что арифметическая инструкция ни при каких условиях не могла бы тратить больше времени, чем выполнение CPUID для получения информации о производителе или версии. Такая процедура распознавания также сможет обнаруживать тех, кто использует смещение/масштабирование TSC.
Повторюсь, разработчикам нужно было бы принудительно включить привязку к вычислительному ядру для выполнения этой проверки на одном ядре, отключить прерывания и принудительно задать IRQL максимальное значение, чтобы гарантировать согласующиеся и надёжные данные. Было бы удивительно, если бы разработчики BattlEye решили реализовать это, потому что для этого требуется гораздо больше усилий. В драйвере ядра BattlEye ест две другие процедуры распознавания виртуальных машин, но это тема для другой статьи.