Всем привет. В этой статье мы разберем некоторые механизмы защиты, которые используются в драйвере EasyAntiCheat_EOS'a ( был использован растовский еак, дата билда ~ 10 марта этого года )
Основываясь на результате обратной разработки, мы рассмотрим:
1. Сбор телеметрии
2. Проверка целостности системных драйверов
3. Шифрование репортов
* Сбор телеметрии:
Еак собирает информацию о каждом запущенном процессе и отправляет информацию о нем на сервер. Для этого он использует LoadImageNotify в качестве каллбека для того, чтобы собирать информацию о запускаемых процессах
Анти-чит собирает телеметрию о процессах для того, чтобы выявлять потенциально опасные процессы ( известные хаки, инжекторы и тд ), составление паттерна поведения у разных пользователей. Также, если игра по какой-либо причине вылетит, то эта информация позволит помочь какой именно процесс мог стать причиной краша.
Псевдокод:
1. Копирование информации о процессе ( парс пе хеадера процесса в нужный формат, копирование названия процесса ( если установлен флаг дополнительной информации ) )
2. Выделение пулла, заполнение его информацией
3. Отправление на сервер
* Проверка целостности системных драйверов:
Еак проходится по каждому загруженному драйверу ( он их получает с помощью zw_query_system_information ( SystemModuleInformation ) ), получает их DriverObject и проходится по каждому MajorFunction, проверяя адрес диспатча )
Анти-чит делает это для того, чтобы выявлять хуки функций драйверов. Чаще всего они используются для создания спуферов или коммуникации с юзермодом.
Псевдокод:
1. Проверка нa то, находится ли функция в модуле
2. Копирование информации о драйвере
3. Отправление репорта, если status_code != 7 || !found ( отправление репорта находится в той функции, где вызывается проверка на диспатч )
* Шифрование репортов:
Еак создает энкриптнутый ключ, используя KUSER_SHARED_DATA значения
Анти-чит делает это для того, чтобы скрыть настоящую информацию, которая передается на сервер, предотвращая легкий способ эмуляции
Псевдокод:
Генерация энкриптнутого ключа для дальнейшего шифрования буфера с репортом.
Вывод:
- С помощью данной статьи вы можете узнать полезную информацию о EasyAntiCheat'e. Надеюсь, эта статья была полезной!
Основываясь на результате обратной разработки, мы рассмотрим:
1. Сбор телеметрии
2. Проверка целостности системных драйверов
3. Шифрование репортов
* Сбор телеметрии:
Еак собирает информацию о каждом запущенном процессе и отправляет информацию о нем на сервер. Для этого он использует LoadImageNotify в качестве каллбека для того, чтобы собирать информацию о запускаемых процессах
Анти-чит собирает телеметрию о процессах для того, чтобы выявлять потенциально опасные процессы ( известные хаки, инжекторы и тд ), составление паттерна поведения у разных пользователей. Также, если игра по какой-либо причине вылетит, то эта информация позволит помочь какой именно процесс мог стать причиной краша.
Псевдокод:
C++:
void collect_process_telemetry( PEPROCESS process, bool use_extended_info ) {
const auto packet = allocate_pool( 0x330 );
if ( memory::is_valid( packet ) ) {
// Получаем информацию о процессе
const auto is_wow64 = PsGetProcessWow64Process( process ) != 0;
const auto pid = PsGetProcessId( process );
const auto process_info = copy_process_info( 0, 0, 0, 0x801, is_wow64, 0, pid, 0 );
// Если включен флаг с дополнительной информацией, то копируем в буфер с данными еще и название процесса
if ( memory::is_valid( process_info ) ) {
if ( use_extended_info ) {
char buffer[ 16 ]{ };
// PsGetProcessImageFileName
if ( get_process_name( process, buffer ) ) {
auto dest = process_info + 0x20;
for ( int idx{ }; idx < 15 && buffer[ idx ]; ++idx )
dest[ idx ] = ( wchar_t )buffer[ idx ];
dest[ 15 ] = 0;
}
}
}
// Заполняем хеадер пакета
*( uint64_t* )pool = process_info; // Указатель на буфер с данными
*( DWORD* )( pool + 0x28 ) = 1; // Статус
*( DWORD* )( pool + 0x30 ) = 0x7429AC76; // Тип пакета
*( DWORD* )( pool + 0x40 ) = 0; // Флаги (?)
*( uint64_t* )( pool + 0x38 ) = 0; // Дополнительная информация
// Отправляем пакет на сервер
send_telemetry_packet( pool, 0 );
ex_free_pool_with_tag( process_info );
}
}
1. Копирование информации о процессе ( парс пе хеадера процесса в нужный формат, копирование названия процесса ( если установлен флаг дополнительной информации ) )
2. Выделение пулла, заполнение его информацией
3. Отправление на сервер
* Проверка целостности системных драйверов:
Еак проходится по каждому загруженному драйверу ( он их получает с помощью zw_query_system_information ( SystemModuleInformation ) ), получает их DriverObject и проходится по каждому MajorFunction, проверяя адрес диспатча )
Анти-чит делает это для того, чтобы выявлять хуки функций драйверов. Чаще всего они используются для создания спуферов или коммуникации с юзермодом.
Псевдокод:
C++:
bool check_driver_dispatch( PDRIVER_OBJECT driver_object, uint32_t index, void* buffer, uint32_t* status_code ) {
if ( status_code )
*status_code = 0;
if ( !driver_object ) {
if ( status_code )
*status_code = 2; // driver_object драйвера не найден
return false;
}
const auto major_function = driver_object->MajorFunction[ index ];
if ( !major_function ) {
if ( status_code )
*status_code = 4; // Функция с таким индексом не имеет реализации
return false;
}
const auto base_address = driver_object->DriverStart;
const auto size = driver_object->DriverSize;
if ( !base_address
|| !size ) {
if ( status_code )
*status_code = 5; // Не удалось определить границы драйвера
return false;
}
bool found{ };
if ( major_function >= base_address && major_function <= ( base_address + size ) ) {
if ( status_code )
*status_code = 7; // Проверка пройдена, функция не была перехвачена
found = true;
if ( buffer ) {
*( uint32_t* )buffer = major_function - base_address; // Сохраняем RVA функции
*( uint32_t* )( ( uintptr_t )buffer + 4 ) = size; // Сохраняем размер драйвера
memcpy( ( char* )buffer + 8, get_module_name( base_address ), 255 ); // Копируем имя модуля
}
} else
if ( status_code )
*status_code = 6; // Функция указывает в чужую память => детект
return found;
}
1. Проверка нa то, находится ли функция в модуле
2. Копирование информации о драйвере
3. Отправление репорта, если status_code != 7 || !found ( отправление репорта находится в той функции, где вызывается проверка на диспатч )
* Шифрование репортов:
Еак создает энкриптнутый ключ, используя KUSER_SHARED_DATA значения
Анти-чит делает это для того, чтобы скрыть настоящую информацию, которая передается на сервер, предотвращая легкий способ эмуляции
Псевдокод:
C++:
void create_encrypted_key( ) {
const auto buffer = reinterpret_cast< uint8_t* >( allocate_pool( 0x1000 ) );
if ( !buffer )
return;
// 0x2C4 -> SystemExpirationDate
// 0x14 -> SystemTime
// 0x260 -> BootId
const auto v16 = *( ULONG* ) 0xFFFFF780000002C4 ^ *( ULONG* )0xFFFFF78000000014;
const auto v17 = *( ULONG* )0xFFFFF78000000260 ^ *( ULONG* )0xFFFFF78000000008;
const auto v18 = ( 1533831705 * ( buffer >> 2 ) ) ^ *( LONG* )0xFFFFF78000000018;
const auto v19 = *( LONG* )0xFFFFF7800000000C ^ ( 1533831705 * ( ( unsigned __int64 )&buffer >> 2 ) );
buffer[ 4 ] = v18 ^ 0x97B9BC33;
*buffer = -96604097;
buffer[ 1 ] = -345804981;
buffer[ 5 ] = v17 ^ 0xE220A00D;
buffer[ 2 ] = 144253742;
buffer[ 7 ] = 897887310;
buffer[ 3 ] = v16 ^ 0xA868AC37;
buffer[ 8 ] = -123907290;
buffer[ 6 ] = v19 ^ 0x79BB2376;
/*
v15 = buffer;
v20 = v19 ^ v16 ^ v18 ^ v17 ^ 0x4C53A039;
*/
}
Генерация энкриптнутого ключа для дальнейшего шифрования буфера с репортом.
Вывод:
- С помощью данной статьи вы можете узнать полезную информацию о EasyAntiCheat'e. Надеюсь, эта статья была полезной!
Последнее редактирование: