Easy Anti Cheat, вероятно, самый популярный kernel античит, он используется во многих играх и принадлежит Epic Games. Он лучше, чем Battleye, поэтому его труднее обойти. Если вы хотите это сделать, у вас должен быть драйвер ядра. Если в игре есть easy anticheat, вы не сможете внедрить, подключить отладчик, в том числе Cheat Engine, или сделать что-либо еще с игровым процессом, пока вы не обойдете EAC.
При внедрении драйвера не забывайте очистить следы, включая PiDDBCacheTable, потому что они обнаруживают вас на основе этого.
Игры, использующие Easy Anti Cheat:
- Apex Legends
- Fortnite
- Rust
- Paladins
- Dead by Daylight
- For Honor
- Gears 5
- Ghost Recon Wildlands
- The Division 2
- ArcheAge
- Cabal Online
- Combat Arms
- Darkfall: Rise of Agaon
- Dauntless
- Dirty Bomb
- Xenoverse 2
- FlyFF Online
- Halo Mastier Chief Collection
- Special Force 2
- и многие другие.
Возможности Easy Anti Cheat:
- Блокировка любого взаимодействия с игровым процессом.
- Блокировка создания дескрипторов процесса.
- Сканирование на наличие скрытых процессов и модулей.
- Сканирование известных подозрительных модулей DLL.
- Сканирование известных подозрительных драйверов.
- Получение списка всех открытых дескрипторов.
- Поиск дисков и устройств.
- Запись всех загруженных драйверов.
- Сбор информации о HWID.
- Обнаружение отладчиков.
- Обнаружение драйверов, внедренных методом Manual Map.
- Обнаружение следов драйверов.
- Проверка исправности ядра.
- Поиск дескрипторов физической памяти.
- Обнаружение модулей с помощью VirtualProtect.
- Проверка окон для обнаружения подозрительных наложений.
- Проверка подозрительных разделов разделяемой памяти.
- Обнаружение хуков.
- Проверка сервисов.
- Сканирование системных потоков.
- Обнаружение внедренных модулей.
- Обнаружение загрузчика драйверов Turla.
- Обнаружение Hypervisor & VM.
- Патч DbgUiRemoteBreakin.
- PsGetProcessDebugPort.
- Чтение DR6 и DR7.
Обнаружение подключенных драйверов:
- PiDDBCacheTable и MmUnloadedDrivers.
- Обнаружение системного пула.
- Обнаружение системного потока.
EAC HWID Generation:
- KUSER_SHARED_DATA.ProcessorFeatures (0xFFFFF78000000274).
- Реестр.
- WMI.
- Версия ntoskernl.exe.
- MAC-адрес.
Ключи реестра для HWID:
- HKEY_LOCAL_MACHINE\Hardware\Description\System\CentralProcessor\0
- HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0
- Identifier
- SerialNumber
- SystemManufacturer
- Computer\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SystemInformation
- Computer\HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\BIOS
- BIOSVendor
- BIOSReleaseDate
- ProductId
- ProcessorNameString
- Computer\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Class{4d36e968-e325-11ce-bfc1-08002be10318}\0000
- Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
- Computer\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\WindowsUpdate
- Registry\Machine\System\CurrentControlSet\Control\Class{4d36e972-e325-11ce-bfc1-08002be10318}\0001
- Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion\Windows Activation Technologies\AdminObject\Store
EAC Hypervisor и обнаружение VM - secret.club.

IA32_EFER
Мы обратили внимание, что EAC после ~ 30 минут игры запросил IA32_EFER. Мы подождали немного дольше, чтобы увидеть, поступили ли еще какие-либо операции чтения / записи в MSR, но после 40 минут ожидания стало ясно, что больше ничего не поступает. Ниже показано уведомление, полученное в программе iPower's Tracer.
IofCallDriver/NtDeviceIoControlFile
CVEAC-2020: обход проверок целостности EasyAntiCheat
«Разработчики читов особенно заинтересованы в проверках самоконтроля против читов. Если вы можете их обойти, вы сможете эффективно исправить или «перехватить» любой античит-код, который может привести к кику или даже бану. В случае с EasyAntiCheat, они используют драйвер режима ядра, который содержит некоторые интересные процедуры обнаружения. Мы собираемся изучить, как работают их проверки целостности и как их обходить, что позволяет эффективно отключить античит.
Процесс реверс-инжиниринга
Первое, что нужно сделать, это определить, есть ли какая-либо проверка целостности. Самый простой способ - пропатчить любой байт из .text и посмотреть, решит ли античит выгнать или забанить вас через некоторое время. Примерно через 10-40 секунд после того, как я исправил случайную функцию, меня выгнали, показав, что они действительно проводят проверки целостности в своем модуле ядра. С помощью моего отладчика на основе гипервизора, который использует возможности EPT, я установил точку остановки памяти для функции, которая была вызвана их подпрограммой уведомления LoadImage (см.PsSetLoadImageNotifyRoutine). Через некоторое время я смог найти, где они обращались к памяти.
Изучив внешние ссылки в IDA Pro и установив некоторые точки остановки для инструкций, я обнаружил, откуда вызывается функция проверки целостности, одна из которых находится внутри подпрограммы уведомления CreateProcess (см.PsSetCreateProcessNotifyRoutine). Эта процедура заботится о некоторых частях инициализации античита, таких как создание внутренних структур, которые будут использоваться для представления игрового процесса.
Список подозрительных модулей, на которые регистрирует EAC (от adriayny)
- Dumper.dll
- Glob.dll
- mswsock.dll
- perl512.dll
- vmclientcore.dll
- vmwarewui.dll
- virtualbox.dll
- qtcorevbox4.dll
- vboxvmm.dll
- netredirect.dll
- atmfd.dll
- cdd.dll
- rdpdd.dll
- vga.dll
- workerdd.dll
- msvbvm60.dll
if ( AttachToProcess(process, (__int64)&v5) )
{
if ( GetUsermodeModule((UNICODE_STRING *)(StringTable + 4830))// Dumper.dll
&& GetUsermodeModule((UNICODE_STRING *)(StringTable + 4852))// Glob.dll
&& GetUsermodeModule((UNICODE_STRING *)(StringTable + 4870))// mswsock.dll
&& GetUsermodeModule((UNICODE_STRING *)(StringTable + 4894))// perl512.dll
|| GetUsermodeModule((UNICODE_STRING *)(StringTable + 4918))// vmclientcore.dll
|| GetUsermodeModule((UNICODE_STRING *)(StringTable + 4952))// vmwarewui.dll
|| GetUsermodeModule((UNICODE_STRING *)(StringTable + 4980))// virtualbox.dll
|| GetUsermodeModule((UNICODE_STRING *)(StringTable + 5010))// qtcorevbox4.dll
|| GetUsermodeModule((UNICODE_STRING *)(StringTable + 5042))// vboxvmm.dll
|| GetUsermodeModule((UNICODE_STRING *)(StringTable + 5066)) )// netredirect.dll
{
v3 = 1;
}
Некоторые драйверы
- Dbgv.sys
- PROCMON23.sys
- dbk64.sys
LOBYTE(v11) = 1;
if ( !(unsigned int)strstr2((__int64)&a1, (const char *)(StringTable + 8038), v11) )// Dbgv.sys
break;
LOBYTE(v16) = 1;
if ( !(unsigned int)strstr2((__int64)&a1, (const char *)(StringTable + 8047), v16) )// PROCMON23.sys
break;
LOBYTE(v17) = 1;
if ( !(unsigned int)strstr2((__int64)&a1, (const char *)(StringTable + 8061), v17) )// dbk64.sys
break;
Хуки пользовательского режима EAC:
hk_BaseThreadInitThunk (Kernel32ThreadInitThunkFunction - ntdll.dll)
hk_D3DXCreateFontA (EAT Hook)
hk_D3DXCreateFontIndirectA (EAT Hook)
hk_D3DXCreateSprite (EAT Hook)
hk_D3DXCreateTextureFromFileInMemory (EAT Hook)
hk_D3DXCreateTextureFromFileInMemoryEx (EAT Hook)
hk_D3DXLoadSurfaceFromMemory (EAT Hook)
hk_Dllmain_mono_dll (Inline Hook)
hk_LoadAppInitDlls (Inline Hook)
hk_LoadLibraryExW_user32 (IAT Hook - user32.dll)
hk_LoadLibraryExW_ws2_32 (IAT Hook - ws2_32.dll)
hk_LockResource_kernel32 (IAT Hook - kernel32.dll)
hk_NtCreateFile_kernelbase (IAT Hook - kernelbase.dll)
hk_NtDeviceIoControlFile_mswsock (IAT Hook - mswsock.dll)
hk_NtOpenFile_kernelbase (IAT Hook - kernelbase.dll)
hk_NtProtectVirtualMemory_kernelbase (IAT Hook - kernelbase.dll)
hk_NtQueryDirectoryFile_kernelbase (IAT Hook - kernelbase.dll)
hk_NtUserGetAsyncKeyState_user32 (IAT Hook - user32.dll)
hk_NtUserSendInput_user32 (IAT Hook - user32.dll)
hk_QueryPerformanceCounter (IAT Hook - game.exe)
hk_RtlExitUserProcess_kernel32 (IAT Hook - kernel32.dll)
hk_VirtualAlloc_iat_kernel32 (IAT Hook - kernel32.dll)
hk_mono_assembly_load_from_full (Inline Hook)
hk_mono_assembly_open_full (Inline Hook)
hk_mono_class_from_name (Inline Hook)
hk_mono_runtime_invoke (Inline Hook)
Процедура обнаружения подозрительных потоков EAC для кода, отображаемого вручную
API-интерфейсы, используемые для перечисления потоков и открытия для них дескрипторов: CreateToolhelp32Snapshot, Thread32First, Thread32Next, OpenThread.
Получение информации о потоке: NtQueryInformationThread (ThreadBasicInformation и ThreadQuerySetWin32StartAddress)
Перемещение по стеку: GetThreadContext, RtlLookupFunctionEntry и RtlVirtualUnwind
Шаги по обнаружению подозрительных потоков:
Получение информации от всех потоков в текущем процессе (идентификатор потока, информация о стеке, базовый адрес потока).
// получение информации
if ( thread_info_obtained )
{
thread_info.ExitStatus = thread_basic_info.ExitStatus;
thread_info.TebBaseAddress = (__int64)thread_basic_info.TebBaseAddress;
thread_info.Priority = thread_basic_info.Priority;
thread_info.BasePriority = thread_basic_info.BasePriority;
thread_info.StartAddress = v18;
if ( thread_basic_info.TebBaseAddress )
{
thread_info.StackBase = *((_QWORD *)thread_basic_info.TebBaseAddress + 1);
thread_info.StackLimit = *((_QWORD *)thread_basic_info.TebBaseAddress + 2);
}
stack_walk_thread(*v8, v14, &thread_info.RipsStackWalk);
LABEL_22:
v15 = v1->CurrentEntry;
if ( v1->LastEntry == v15 )
{
reallocate_vector_thread_information(v1, v15, &thread_info);
}
else
{
memcpy_thread_information(v11, v15, &thread_info);
++v1->CurrentEntry;
}
}
reset_thread_information_struct(&thread_info);
++v8;
v19 = v8;
}
// перемещение по стеку
run_count = 0;
while ( run_count < 9 )
{
entry_pc = RtlLookupFunctionEntry(Context.Rip, &v20, 0i64);
if ( entry_pc )
{
RtlVirtualUnwind_0(0i64, v20, Context.Rip, entry_pc, &Context, &v23, &v22, 0i64, thread_rip_1);
if ( !Context.Rip )
return vec_rips_stackwalk->FirstEntry != vec_rips_stackwalk->CurrentEntry;
thread_rip_1 = Context.Rip;
current_entry = vec_rips_stackwalk->CurrentEntry;
if ( vec_rips_stackwalk->LastEntry == current_entry )
{
reallocate_vector_qword(vec_rips_stackwalk, current_entry, &thread_rip_1);
}
else
{
*current_entry = Context.Rip;
++vec_rips_stackwalk->CurrentEntry;
}
}
++run_count;
RtlLookupFunctionEntry = *(__int64 (__fastcall **)(DWORD64, __int64 *, _QWORD))RtlLookupFunctionEntry_0;
}
return vec_rips_stackwalk->FirstEntry != vec_rips_stackwalk->CurrentEntry;
Поиск подозрительных потоков по начальным адресам / обхода разрыва стека за пределами диапазонов модулей.
У вас нет обхода EAC, если вы не скрываетесь от этого.
// начало проверки
start_address = thread_info_1->StartAddress;
if ( start_address
&& (unsigned __int8)get_region_from_address(start_address, memory_region_info_vec_1, &memory_region_info_) )
{
if ( (memory_region_info_.mbi.Protect & 0x10
|| memory_region_info_.mbi.Protect & 0x20
|| memory_region_info_.mbi.Protect & 0x40) //executable region
&& !memory_region_info_.DllName.Length ) //not associated with a module
{
// копирование данных
}
////////////////////////////////////////////////////////////////////////
// проверка разрыва стека
entry = thread_info_1->RipsStackWalk.FirstEntry;
current_entry = thread_info_1->RipsStackWalk.CurrentEntry;
while ( entry != current_entry )
{
if ( *entry
&& (unsigned __int8)get_region_from_address(*entry, memory_region_info_vec_1, &memory_region_info_)
&& (memory_region_info_.mbi.Protect & 0x10
|| memory_region_info_.mbi.Protect & 0x20
|| memory_region_info_.mbi.Protect & 0x40) //executable region
&& !memory_region_info_.DllName.Length ) //not associated with a module
{
// копирование данных
}
//...
}
//...
Копирование данных и отправка на свой сервер.
//...
CEasyAntiCheat::send(eac_instance,
281i64,
Dst.FirstEntry,
(unsigned int)(LODWORD(Dst.CurrentEntry) - LODWORD(Dst.FirstEntry)));
//...
Надеюсь, прочитав все это, вы поймете, что создание обхода EAC - непростая задача. Конечно, вы можете войти в ядро и получить доступ к процессу игры. Но EAC знает, что вы делаете, и забанить вас - лишь вопрос времени. Если вы продаете читы там, где у вас много пользователей, вам нужно убедиться, что ваш обход EAC идеален.
Статья переведена @antihero с англоязычного форума, возможны некоторые ошибки. Если вы их нашли, пожалуйста, воспользуйтесь личными сообщениями на форуме и проинформируйте меня.
Источник.