La proliferación de cheats en videojuegos competitivos ha forzado a la industria a adoptar soluciones de seguridad que operan en los niveles de privilegio más bajos del sistema operativo, específicamente en el kernel de Windows. Este movimiento, aunque efectivo contra la mayoría de los cheats de usuario y kernel, introduce una complejidad significativa y plantea preocupaciones sobre la superficie de ataque y la privacidad. La tesis central es que la seguridad del juego, en este contexto, se ha transformado en una carrera armamentista asimétrica donde los defensores deben cubrir todas las posibles vulnerabilidades, mientras que los atacantes solo necesitan encontrar una. Esta asimetría impulsa la necesidad de una arquitectura de defensa en capas que abarca desde la intercepción de llamadas al sistema hasta el análisis conductual avanzado.
Históricamente, los cheats comenzaron en el espacio de usuario (ring 3), siendo fácilmente detectables por anti-cheats del mismo nivel. Sin embargo, los desarrolladores de cheats rápidamente migraron al kernel (ring 0) para obtener control total sobre el sistema y evadir las detecciones. La respuesta inevitable fue que los anti-cheats también se movieran al kernel, replicando las mismas técnicas de intrusión que los cheats maliciosos. Esta escalada ha llevado a un sofisticado ecosistema de detección y evasión que se asemeja a la lucha contra el malware persistente, donde la visibilidad a nivel de kernel es fundamental para la detección.
Arquitectura del Sistema
Los sistemas anti-cheat modernos adoptan un modelo de tres componentes: un driver de kernel (ring 0), un servicio de usermode (SYSTEM privileges) y una DLL inyectada en el juego (ring 3). El driver de kernel, como BEDaisy.sys de BattlEye o vgk.sys de Vanguard, es el componente con mayor privilegio, encargado de registrar callbacks del kernel (ObRegisterCallbacks, PsSetCreateProcessNotifyRoutineEx, PsSetLoadImageNotifyRoutine, CmRegisterCallbackEx), interceptar llamadas al sistema, escanear la memoria (VAD walking, PoolBigPageTable) y aplicar protecciones. Se comunica con el servicio de usermode a través de IOCTLs.
El servicio de usermode, como BEService.exe o vgauth.exe, maneja la lógica de aplicación más compleja, la comunicación de red con los servidores backend, la gestión de baneos y la recolección de telemetría. Se comunica con la DLL inyectada en el juego mediante Named Pipes y Shared Memory (NtCreateSection, NtMapViewOfSection) para un intercambio de datos de alta velocidad y baja latencia. La DLL inyectada realiza verificaciones en el lado del usuario, interactúa con el servicio y sirve como punto final para las protecciones específicas del proceso del juego. La carga del driver puede ser en tiempo de ejecución (BattlEye, EAC) o en tiempo de arranque (Vanguard), siendo esta última más agresiva al permitir la observación de todos los drivers que se cargan posteriormente. La integridad del sistema se verifica mediante el cumplimiento de los requisitos de firma de drivers (Driver Signature Enforcement) y la detección de técnicas de evasión como BYOVD (Bring Your Own Vulnerable Driver) y la manipulación de estructuras internas del kernel como PiDDBCacheTable y MmUnloadedDrivers.
Flujo de Detección y Mitigación de Cheats
- 1 Driver Kernel Registra callbacks (ObRegisterCallbacks, PsSetCreateProcessNotifyRoutineEx) p...
- 2 Driver Kernel Escanea memoria (VAD walking, hashing de secciones de código) para detectar i...
- 3 Driver Kernel Detecta hooks (IAT, inline, SSDT) y manipulaciones de estructuras internas (P...
- 4 Driver Kernel Captura eventos de entrada (mouse, teclado) y los escribe en Shared Memory.
- 5 Servicio Usermode Lee eventos de Shared Memory, los agrupa, encripta y envía a backend.
- 6 Backend ML Ejecuta inferencia de ML sobre la telemetría para generar puntuaciones de ano...
- 7 Servicio Usermode Recibe decisiones de ban/kick del backend y las aplica al cliente del juego.
| Capa | Tecnología | Justificación |
|---|---|---|
| security | Windows Kernel Callbacks | Proporcionan visibilidad en tiempo real de eventos críticos del sistema (creación de procesos/hilos, carga de imágenes, operaciones de handles, registro) y permiten la intercepción y modificación de operaciones. vs User-mode hooks (fácilmente evadibles), Polling (ineficiente, con ventanas de oportunidad) ObRegisterCallbacks (PsProcessType, PsThreadType), PsSetCreateProcessNotifyRoutineEx, PsSetCreateThreadNotifyRoutine, PsSetLoadImageNotifyRoutine, CmRegisterCallbackEx, Minifilter Drivers. |
| security | Virtual Address Descriptor (VAD) Tree Walking | Permite escanear directamente las regiones de memoria de un proceso para detectar código inyectado o mapeado manualmente, incluso si las listas de módulos del PEB han sido manipuladas. vs ZwQueryVirtualMemory (puede ser engañado por cheats), Enumeración de módulos PEB (fácilmente manipulable) Acceso directo a EPROCESS::VadRoot y estructuras MMVAD. |
| security | Driver Signature Enforcement (DSE) & Blocklists | Asegura que solo drivers firmados por autoridades confiables puedan cargarse en el kernel, y bloquea drivers legítimos pero vulnerables usados en ataques BYOVD. vs Ninguna alternativa directa para la integridad del kernel., Solo blocklists (menos proactivo que allowlists) CiValidateImageHeader, DriverSiPolicy.p7b, allowlists de Vanguard. |
| observability | Shared Memory (NtCreateSection, NtMapViewOfSection) | Permite la comunicación de alta velocidad y baja latencia entre el driver de kernel y el servicio de usermode para la transmisión de telemetría. vs IOCTLs (mayor overhead por evento), Named Pipes (mayor latencia para grandes volúmenes) Uso de ring buffers para eficiencia. |
| data-processing | Machine Learning (CNNs, Transformers) | Analiza patrones conductuales de entrada (movimiento del ratón, tiempos de reacción) para detectar aimbots y triggerbots que son invisibles a las detecciones estáticas del kernel. vs Heurísticas fijas (fácilmente evadibles), Detección manual (no escalable) Modelos entrenados con series temporales de datos de entrada, características como posición, velocidad, aceleración, ángulos de vista. |
Trade-offs
Ganancias
- ▲ Precisión de detección de cheats
- ▲ Resistencia a la evasión
- ▲▲ Cobertura de la superficie de ataque
Costes
- ▲ Superficie de ataque del sistema
- ▲ Complejidad de desarrollo y mantenimiento
- ▲ Preocupaciones de privacidad del usuario
- △ Requisitos de recursos del sistema
OB_CALLBACK_REGISTRATION callbackReg = {0};
OB_OPERATION_REGISTRATION opReg[2] = {0};
UNICODE_STRING altitude = RTL_CONSTANT_STRING(L"31001");
opReg[0].ObjectType = PsProcessType;
opReg[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
opReg[0].PreOperation = ObPreOperationCallback;
opReg[0].PostOperation = ObPostOperationCallback;
opReg[1].ObjectType = PsThreadType;
opReg[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
opReg[1].PreOperation = ObPreOperationCallback;
opReg[1].PostOperation = NULL;
callbackReg.Version = OB_FLT_REGISTRATION_VERSION;
callbackReg.OperationRegistrationCount = 2;
callbackReg.Altitude = altitude;
callbackReg.RegistrationContext = NULL;
callbackReg.OperationRegistration = opReg;
NTSTATUS status = ObRegisterCallbacks(&callbackReg, &gCallbackHandle);// Walk the VAD tree to find executable anonymous mappings
VOID ScanForManuallyMappedCode(PEPROCESS Process)
{
KAPC_STATE apcState;
KeStackAttachProcess(Process, &apcState);
PVOID baseAddress = NULL;
MEMORY_BASIC_INFORMATION mbi;
while (NT_SUCCESS(ZwQueryVirtualMemory(
ZwCurrentProcess(),
baseAddress,
MemoryBasicInformation,
&mbi,
sizeof(mbi),
NULL)))
{
if (mbi.State == MEM_COMMIT &&
(mbi.Protect & PAGE_EXECUTE_READ ||
mbi.Protect & PAGE_EXECUTE_READWRITE ||
mbi.Protect & PAGE_EXECUTE_WRITECOPY) &&
mbi.Type == MEM_PRIVATE) // Private, not file-backed
{
ReportSuspiciousRegion(mbi.BaseAddress, mbi.RegionSize,
"Executable private memory without file backing");
}
baseAddress = (PVOID)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
if ((ULONG_PTR)baseAddress >= 0x7FFFFFFFFFFF) break; // User space limit
}
KeUnstackDetachProcess(&apcState);
}BOOLEAN IsKernelDebuggerPresent(void)
{
// KD_DEBUGGER_ENABLED is a kernel export
if (*KdDebuggerEnabled && !*KdDebuggerNotPresent) {
return TRUE;
}
// Additional check: attempt a debug break and see if it's handled
// More sophisticated: check specific kernel structures
return FALSE;
}Fundamentos Teóricos
La arquitectura de los anti-cheats a nivel de kernel se alinea con los principios de los rootkits defensivos, un concepto explorado en el paper "If It Looks Like a Rootkit and Deceives Like a Rootkit" (ARES 2024). Este trabajo clasifica los anti-cheats como software con características técnicas de rootkit debido a su operación a nivel de kernel, registro de callbacks a nivel de sistema y amplia visibilidad de la actividad del OS. La necesidad de estas primitivas se deriva de la arquitectura de seguridad de Windows, donde el kernel es el único punto de control confiable.
La detección de patrones de movimiento del ratón y tiempos de reacción para identificar aimbots y triggerbots se basa en principios de psicofísica y ergonomía, como la Ley de Fitts, que describe la relación entre el tiempo para moverse a un objetivo y su distancia/tamaño. La aplicación de Machine Learning, incluyendo CNNs (Collins et al., CheckMATE 2024) y arquitecturas Transformer (Sousa et al., AntiCheatPT 2025), para el análisis conductual de la telemetría de juego, conecta directamente con la investigación académica en detección de anomalías y procesamiento de series temporales, buscando patrones que se desvían del comportamiento humano esperado.