Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
  • Данный раздел создан исключительно для релизов! Вопросы по лаунчеру или обвязке задавайте ТОЛЬКО в соответсвующей теме автора. Любые другие темы будут удалены, а авторы понесут наказание.

Лаунчер [C++17|Avn2]: Пишем защиту вместе! #7 Виртуализация на службе у читера

woz4tb.jpg
 
Хотелось бы спросить, будет ли возможность сделать защиту в виде драйвера, сертификат для подписывания exe имеется.
 
Хотелось бы спросить, будет ли возможность сделать защиту в виде драйвера
Да, без проблем. Какой функционал требуется именно от драйвера?
сертификат для подписывания exe имеется.
Сертификат твой личный и EV? Потому что тебе потребуется дополнительно подписывать драйвер на портале Microsoft, чтобы была возможность загружаться на машинах с включенным SecureBoot или HVCI/DeviceGuard (для некоторых настроек последнего дополнительно придётся проходить сертификацию WHQL). Для этого нужно регистрировать на портале свою компанию, на которую оформлен сертификат, и сертификат, разумеется, должен быть EV. Если сертификат обычный (не-EV) - сможешь загружаться только на машинах без SecureBoot'a.
 
Да, без проблем. Какой функционал требуется именно от драйвера?

Сертификат твой личный и EV? Потому что тебе потребуется дополнительно подписывать драйвер на портале Microsoft, чтобы была возможность загружаться на машинах с включенным SecureBoot или HVCI/DeviceGuard (для некоторых настроек последнего дополнительно придётся проходить сертификацию WHQL). Для этого нужно регистрировать на портале свою компанию, на которую оформлен сертификат, и сертификат, разумеется, должен быть EV. Если сертификат обычный (не-EV) - сможешь загружаться только на машинах без SecureBoot'a.
Будет EV, пока не ev
 
ну чтобы драйвер уже чекал инжекты
 
ну чтобы драйвер уже чекал инжекты
А смысл? Почему именно в драйвере? Драйвер стоит использовать для задач, которые нельзя сделать в юзермоде. Проверять инжекты в драйвере и вполовину не так удобно, как в юзермоде (и возможностей меньше), но в драйвере можно блокировать доступ к процессу со стороны - например, для защиты от читэнджина и других редакторов памяти. Этот функционал уже готов в не связанном с антиинжектами проекте - Kernel-Bridge (посмотри у меня на гитхабе).
 
Хошимин (вносит импакт в развитие ведра и античит индустрии)
p.s. Как давно я тут ничего не писал
 
А смысл? Почему именно в драйвере? Драйвер стоит использовать для задач, которые нельзя сделать в юзермоде. Проверять инжекты в драйвере и вполовину не так удобно, как в юзермоде (и возможностей меньше), но в драйвере можно блокировать доступ к процессу со стороны - например, для защиты от читэнджина и других редакторов памяти. Этот функционал уже готов в не связанном с антиинжектами проекте - Kernel-Bridge (посмотри у меня на гитхабе).
От читэнжина можно нормально защититься только драйвером или все же можно как-то нативно?
 
От читэнжина можно нормально защититься только драйвером или все же можно как-то нативно?
Без драйвера нельзя, с драйвером можно, но тоже не на 100%. Если пересоберут драйвер читэнджина, сменят имена девайсов - обойдут защиту, потому что в ядре нет простых способов задетектить, что кто-то читает память. Например, в моём драйвере можно поставить фильтр на функцию DeviceIoControl, через которую читэнджин общается со своим драйвером, но как отличить драйвер читэнджина от любого другого системного драйвера - вопрос открытый. Разве что сигнатурами, которые легко обходятся, стоит читеру накрыть драйвер криптором.
 
Проще бороться с самими последствиями работы CheatEngine, проверяя корректность тех участков памяти, которые читеры меняют чаще всего, чем пытаться выявить модифицированный драйвер. Таким способом можно защититься не только от CheatEngine, но и от других читов, меняющих память.
 
Проще бороться с самими последствиями работы CheatEngine, проверяя корректность тех участков памяти, которые читеры меняют чаще всего, чем пытаться выявить модифицированный драйвер. Таким способом можно защититься не только от CheatEngine, но и от других читов, меняющих память.
Допустим, игрок меняет координаты. Как проверить их корректность в условиях сервера с модами?
 
Без драйвера нельзя, с драйвером можно, но тоже не на 100%. Если пересоберут драйвер читэнджина, сменят имена девайсов - обойдут защиту, потому что в ядре нет простых способов задетектить, что кто-то читает память. Например, в моём драйвере можно поставить фильтр на функцию DeviceIoControl, через которую читэнджин общается со своим драйвером, но как отличить драйвер читэнджина от любого другого системного драйвера - вопрос открытый. Разве что сигнатурами, которые легко обходятся, стоит читеру накрыть драйвер криптором.
СталКрафт, хкс вроде же защитились от ЧитЭнжина. В чем секрет?)
 
СталКрафт, хкс вроде же защитились от ЧитЭнжина. В чем секрет?)
Именно от читэнджина не защитились, но защитились от инжекта классов. На сталкрафте пересобрана джава, все классы (в том числе, классы джавы) зашифрованы, а внутри джавы вшит декриптор. Плюс, вырезано очень много того, что облегчает читерам внедрение классов, и перемешаны поля во внутренних структурах JVM, которые необходимы для работы с JNI (разработчик читов, разумеется, новое расположение полей не знает, а итоговая джава накрыта сверху протектором - например, вмп или темидой). Но именно от редактирования памяти они не защищены, как и не защищены от воллхака (на сталкрафте работают хуки OpenGL). Другое дело, что у них есть проверки и на сервере. Например, на хкс отправляются не координаты игрока, а движение (типа, "сделать шаг вперёд"), а сервер уже сам решает, на сколько сдвинуть игрока - поэтому спидхаки не работают.
Это очень сложная работа, требует модификации джавы, клиента и сервера, поэтому такое себе могут позволить только топовые сервера со штатом разработчиков.
 
Последнее редактирование:
Именно от читэнджина не защитились, но защитились от инжекта классов. На сталкрафте пересобрана джава, все классы (в том числе, классы джавы) зашифрованы, а внутри джавы вшит декриптор. Плюс, вырезано очень много того, что облегчает читерам внедрение классов, и перемешаны поля во внутренних структурах JVM, которые необходимы для работы с JNI (разработчик читов, разумеется, новое расположение полей не знает, а итоговая джава накрыта сверху протектором - например, вмп или темидой). Но именно от редактирования памяти они не защищены, как и не защищены от воллхака (на сталкрафте работают хуки OpenGL). Другое дело, что у них есть проверки и на сервере. Например, на хкс отправляются не координаты игрока, а движение (типа, "сделать шаг вперёд"), а сервер уже сам решает, на сколько сдвинуть игрока - поэтому спидхаки не работают.
Это очень сложная работа, требует модификации джавы, клиента и сервера, поэтому такое себе могут позволить только топовые сервера со штатом разработчиков.
Вот это правильная защита да. Все остальное изи сдуть на стороне клиента.
 
Вот это правильная защита да. Все остальное изи сдуть на стороне клиента.
В паблик такую защиту выкладывать бессмысленно, т.к. требует доработки конкретного клиента и сервера, а мало кто будет с этим заморачиваться, особенно с учётом паблик-модов, которые тоже, вероятно, потребуется допиливать руками. Но да, если продавать конкретному серверу за крупную сумму - только такую защиту и есть смысл писать
 
Но да, если продавать конкретному серверу за крупную сумму - только такую защиту и есть смысл писать
Не хочешь заняться таким ?:В

Коммерческий раздел здесь присутствует, спрос думаю будет.
 
Проще бороться с самими последствиями работы CheatEngine, проверяя корректность тех участков памяти, которые читеры меняют чаще всего, чем пытаться выявить модифицированный драйвер. Таким способом можно защититься не только от CheatEngine, но и от других читов, меняющих память.
Пример такого имеется ?
 
#3: Защищаемся от инжектов через оконные хуки

Третий по популярности способ инжекта библиотек.
Основан на работе функции SetWindowsHookEx, предназначенной для перехвата оконных сообщений, отправляемых оконной подсистемой из ядра соответствующим окнам в пределах экземпляра рабочего стола.

Посмотрим на прототип функции:
C++:
HHOOK SetWindowsHookExA(
    int idHook,
    HOOKPROC lpfn,
    HINSTANCE hmod,
    DWORD dwThreadId
);

Нас интересуют два последних параметра:
hmod - инстанс (базовый адрес) модуля, в котором расположена функция фильтра оконных сообщений, адрес которой передаём в параметре lpfn.
dwThreadId - идентификатор потока, с которым ассоциирован наш оконный фильтр. Если этот параметр выставить равным нулю, оконный фильтр будет ловить все оконные сообщения для всех оконных потоков в пределах экземпляра рабочего стола. В этом случае система должна доставить код фильтра в каждый целевой процесс. Это возможно только в случае, если код фильтра расположен в dll - в этом случае система подгружает эту библиотеку в каждый оконный процесс. Так и происходит инжект.

Напишем простейшую дллку, загружающую себя во все оконные процессы:
C++:
#include <Windows.h>

static HMODULE hInstance = NULL;
static HHOOK hHook = NULL;

static LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
    return CallNextHookEx(hHook, code, wParam, lParam);
}

extern "C"
__declspec(dllexport)
BOOL WINAPI InjectEmAll()
{
    hHook = SetWindowsHookEx(WH_DEBUG, HookProc, hInstance, 0);
    return hHook != NULL;
}

extern "C"
__declspec(dllexport)
BOOL WINAPI UnInjectEmAll()
{
    return UnhookWindowsHookEx(hHook);
}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, PCONTEXT lpContext)
{
    if (dwReason == DLL_PROCESS_DETACH) {
        hInstance = hModule; // Сохраняем инстанс нашей библиотеки
    }
    return TRUE;
}
И, соответственно, приложение, которое загрузит эту библиотеку и внедрит её во все процессы:
C++:
#include <Windows.h>

int main()
{
    // Загружаем библиотеку и импортируем нашу функцию:
    HMODULE hLib = LoadLibrary(L"OurAwesomeLib.dll");
    auto InjectEmAll = reinterpret_cast<BOOL(WINAPI*)()>(GetProcAddress(hLib, "InjectEmAll"));

    // Инжектим нашу дллку во все процессы:
    InjectEmAll();

    return 0;
}

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

Рассмотрим загрузку такой библиотеки в процесс. Так как магии не бывает, вероятнее всего, процесс сам каким-то образом подгружает библиотеку по сигналу из ядра. Поставим брейкпоинт на LoadLibrary в библиотеке kernelbase.dll и посмотрим на трейс потока в случае, когда в процесс загружается библиотека через оконные хуки:
upload_2019-3-25_20-57-57.png​

Мы видим, что процесс уходит в ядро на функции NtUserCallNoParam, после чего ядро выполняет юзермодный каллбэк __ClientLoadLibrary, вызвав его из диспетчера юзермодных каллбэков KiUserCallbackDispatcher:
upload_2019-3-25_21-5-44.png​

Диспетчер каллбэков ищет нужный каллбэк по индексу в таблице юзермодных каллбэков KernelCallbackTable (обведена рамкой), адрес которой можно найти в PEB - Process Environment Block. Эта таблица нужна для хранения адресов юзермодных каллбэков, использующихся в оконной подсистеме, которые ядро может при необходимости вызывать в любом оконном процессе напрямую, используя сервис KeUserModeCallback.

Подытожим вышесказанное:
1. Процесс-инжектор устанавливает глобальные оконные хуки функцией SetWindowsHookEx с обработчиком в дллке, которую хотим подгрузить во все оконные процессы
2. Ядро вызывает KeUserModeCallback для всех оконных процессов, передавая в неё индекс функции __ClientLoadLibrary в таблице PEB->KernelCallbackTable
3. Происходит переключение в юзермод на точку входа KiUserCallbackDispatcher, которая по переданному индексу находит и вызывает __ClientLoadLibrary
4. Внутри __ClientLoadLibrary происходит вызов LoadLibrary из KernelBase
5. После подгрузки библиотеки каллбэк возвращается в ядро через сервис ZwCallbackReturn

Для полноты картины приведём внутреннее устройство __ClientLoadLibrary:
upload_2019-3-25_22-39-19.png
​
Итак, чтобы защититься от таких инжектов, можно пойти двумя путями:
1. Перехватить __ClientLoadLibrary (например, подменить её адрес в KernelCallbackTable)
2. Проверять, откуда вызвана LdrLoadDll, и, если вызвана из __ClientLoadLibrary, запрещать загрузку

Но для начала нужно найти сам адрес __ClientLoadLibrary. Эта функция не экспортируется, но её адрес лежит в KernelCallbackTable. Так как это единственная функция из таблицы, которая вызывает LoadLibrary, мы можем поставить хук на LdrLoadDll и в обработчике проверять каждый фрейм стактрейса, лежит ли он в диапазоне одной из функций в KernelCallbackTable.

Так как размер KernelCallbackTable не определён (как и порядок функций), мы можем прибегнуть к хитрости: перебирать элементы в таблице до тех пор, пока адреса функций лежат внутри user32.dll. Первый не попавший в user32 элемент считаем концом таблицы.

Теперь в обработчике хука LdrLoadDll снимаем трейс функцией CaptureStackBackTrace. Нам хватит 5-6 фреймов. Для каждого фрейма выполняем проверку, псевдокод которой представлен ниже:
C++:
// Проверяем, лежит ли адрес в первых 256 байтах функции:
static inline BOOL IsInFunction(PVOID Ptr, PVOID FuncBase)
{
    return reinterpret_cast<PBYTE>(Ptr) >= reinterpret_cast<PBYTE>(FuncBase)
        && reinterpret_cast<PBYTE>(Ptr) < (reinterpret_cast<PBYTE>(FuncBase) + 256);
}

BOOL IsCalledFromWinHook(PVOID StacktraceFrame)
{
    for (PVOID KernelCallback : KernelCallbackTable) {
        if (IsInFunction(StacktraceFrame, KernelCallback)) {
            return TRUE; // Мы внутри __ClientLoadLibrary
        }
    }
    return FALSE;
}

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

Подводные камни:
Через оконные хуки подгружаются, в том числе, и системные библиотеки (uxtheme.dll), необходимые для корректного рендеринга UI, а также, библиотеки драйверов видеокарт, стримилки, библиотеки от мессенджеров (например, Discord), поэтому бездумное блокирование может повлечь дополнительные проблемы со сторонним софтом. Как вариант, можно разрешать загрузку только подписанных и системных библиотек (на помощь идут функции WinVerifyTrust и SfcIsFileProtected соответственно).
 
Последнее редактирование:

Поделиться этой страницей

Назад
Верх