Cómo configurar anti-tamper (protección) del cliente de MU Online
Blinda el Main.exe de tu servidor de MU Online contra edición, inyección y memory hacks combinando checksum de integridad, anti-debug, verificación de módulos y un handshake firmado con el servidor.
Proteger el cliente de MU Online es una carrera armamentista permanente. Del otro lado están los editores hexadecimales, los inyectores de DLL, los trainers de memoria y los cheats de speed, wallhack y auto-combo que se aprovechan de un Main.exe desprotegido. El anti-tamper es el conjunto de defensa
Proteger el cliente de MU Online es una carrera armamentista permanente. Del otro lado están los editores hexadecimales, los inyectores de DLL, los trainers de memoria y los cheats de speed, wallhack y auto-combo que se aprovechan de un Main.exe desprotegido. El anti-tamper es el conjunto de defensas que dificulta editar el binario, adjuntar un debugger, inyectar código y alterar valores en memoria y, tan importante como eso, permite que el servidor detecte cuándo el cliente no es confiable. En este tutorial vas a montar esas capas de forma práctica: integridad por checksum, anti-debug, verificación de módulos cargados, protección de memoria y un handshake firmado con el servidor. Todos los valores concretos (offsets, puertos, nombres de sección) aparecen como ejemplo y varían por season/cliente.
Requisitos previos
Antes de blindar cualquier cosa, necesitas una base funcional. Si aún estás montando el servidor, comienza por cómo crear un servidor de MU Online y vuelve aquí cuando el cliente ya se conecte normalmente. Blindar un cliente que ni siquiera loguea solo multiplica variables cuando algo se rompa.
| Requisito | Detalle (ejemplo — varía por season/cliente) |
|---|---|
| Cliente funcional | Main.exe que conecta y loguea sin protección todavía |
| Copia de seguridad del binario | Copia intacta Main_original.exe fuera de la carpeta del cliente |
| Herramientas de análisis | HxD, CFF Explorer, x64dbg, Process Hacker |
| Launcher propio | Ejecutable que valida e inicia el cliente (ideal) |
| Acceso al ConnectServer | Para agregar validación server-side |
| Entorno de prueba aislado | Máquina limpia, separada de la de desarrollo |
El ítem menos obvio es el launcher propio. Muchas protecciones quedan mejor en un componente que controlas totalmente y puedes recompilar a voluntad, en vez de intentar remendar el Main.exe que vino en el paquete. El launcher verifica la integridad, conversa con el servidor y solo entonces dispara el cliente.
Modelo de amenazas: contra qué te estás protegiendo
Antes de elegir técnicas, define qué quiere hacer el atacante. Una protección genérica desperdicia CPU y genera falsos positivos; una protección dirigida al ataque real vale el esfuerzo.
| Ataque | Vector típico | Defensa principal |
|---|---|---|
| Editar IP/versión en el binario | HxD, patcher | Checksum de integridad |
| Adjuntar debugger | x64dbg, Cheat Engine | Anti-debug |
| Inyectar DLL de cheat | Injector, LoadLibrary | Verificación de módulos |
| Alterar valores en memoria | Cheat Engine, trainer | Cifrado/ofuscación de variables + validación server-side |
| Speed hack | Aceleradores de clock | Verificación de tiempo + validación server-side |
| Forjar paquetes | Proxy, bot | Handshake firmado + validación server-side |
Fíjate en que la validación server-side aparece en casi todas las líneas. Esa es la lección central: el cliente corre en la máquina del enemigo y nunca será totalmente confiable. El anti-tamper eleva el costo del ataque y alimenta al servidor con señales; el veredicto final es del servidor.
Capa 1 — Integridad por checksum
La defensa más barata contra la edición del binario es comprobar si es bit a bit igual al que distribuiste. Genera un hash fuerte del Main.exe original y verifícalo en dos lugares: en el launcher, antes de iniciar, y en el servidor, durante el handshake.
Paso 1 — Generar el hash de referencia
En Windows, con el binario final ya listo (ya parcheado con IP y versión correctas):
certutil -hashfile Main.exe SHA256
Guarda ese hash. Es la "verdad" contra la cual se comparará todo cliente. Cada vez que reedites el Main.exe, el hash cambia: automatiza la regeneración para no distribuir un cliente con un hash desactualizado.
Paso 2 — Verificar en el launcher
El launcher calcula el SHA-256 del Main.exe en disco y lo compara con el valor esperado antes de ejecutar. Ejemplo en C++ (esbozo):
// Pseudocódigo — varía por season/cliente y por cómo incrustas el hash
bool VerificarIntegridade(const wchar_t* caminho, const std::string& hashEsperado) {
std::string hashAtual = CalcularSHA256(caminho); // via CryptoAPI / bcrypt
if (hashAtual != hashEsperado) {
MostrarErro(L"Cliente adulterado. Descárgalo nuevamente desde el launcher.");
return false;
}
return true;
}
> No incrustes el hash esperado como una cadena en texto plano fácil de encontrar. Ofúscalo, divídelo en partes o derívalo de otra cosa. Un atacante que localiza la cadena simplemente la cambia por el hash de su binario editado.
Paso 3 — Checksum de sección en runtime (self-check)
Además del check en disco, el propio cliente puede comprobar la suma de su sección de código (.text) en memoria mientras corre. Esto atrapa parches aplicados después de la carga (inyección de código). La idea: en el build, calcula el CRC/hash de la sección .text y, en runtime, recalcúlalo periódicamente comparándolo con el valor grabado. Si diverge, repórtalo al servidor.
Como el .text se mapea con relocations y el valor esperado debe coincidir, este check normalmente exige control del proceso de build o un protector que ya lo haga por ti (ver Capa 6).
Capa 2 — Anti-debug
Cheat Engine y x64dbg dependen de adjuntar un debugger al proceso. Detectar esa presencia encarece el análisis dinámico.
Paso 4 — Verificaciones básicas
// Ejemplos clásicos — combina varios, nunca confíes en uno solo
bool DebuggerPresente() {
if (IsDebuggerPresent()) return true;
BOOL remoto = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &remoto);
if (remoto) return true;
// PEB->BeingDebugged y NtGlobalFlag también los verifican las protecciones más profundas
return false;
}
Paso 5 — Detectar herramientas por ventana/proceso
Barre ventanas y procesos en busca de firmas conocidas (nombres de clase y títulos de Cheat Engine, x64dbg, OllyDbg, Process Hacker). Mantén la lista externa y actualizable, porque los nombres cambian.
> No cierres el cliente al detectar. Los overlays de grabación, las herramientas de accesibilidad e incluso algunos antivirus disparan estos checks. El patrón seguro es reportar al servidor con la señal y dejar el ban para el análisis humano o la heurística acumulada. Matar el proceso al instante es la receta para un foro lleno de quejas de jugadores legítimos.
Capa 3 — Verificación de módulos cargados
Los cheats frecuentemente entran como DLL inyectada. Enumerar los módulos del proceso y compararlos con una allowlist detecta inyecciones obvias.
Paso 6 — Enumerar y clasificar módulos
// Esbozo — la allowlist varía MUCHO por season/cliente y por los drivers del usuario
void VarrerModulos() {
// EnumProcessModules -> para cada módulo, GetModuleFileName
// Clasifica: sistema (system32), del propio cliente, y "desconocido"
// Módulos desconocidos y no firmados -> señal para el servidor
}
Cuidado con los falsos positivos: los overlays (Discord, Steam, GPU), los IME y el software de audio inyectan DLLs legítimas. Por eso la salida ideal es un reporte al servidor con los módulos sospechosos, no un bloqueo local ciego. Firmar digitalmente y comprobar la firma (Authenticode) reduce mucho el ruido.
Capa 4 — Protección de valores en memoria
Los trainers editan HP, zen, coordenadas y velocidad directo en la RAM con Cheat Engine. No impides la lectura, pero dificultas la escritura útil.
- No guardes valores sensibles en texto plano. Almacena HP, zen y velocidad cifrados/ofuscados y descífralos solo al usarlos. Cheat Engine encuentra el valor
1000fácil; encontrar el valor cifrado que cambia de representación, no tanto. - Guarda una copia sombra. Mantén un segundo valor derivado (suma de verificación) y, periódicamente, comprueba si coinciden. Divergencia = alguien escribió directamente.
- Valida en el servidor. El HP, el zen, la posición y la velocidad que solo existen en el cliente son inútiles: el servidor debe ser la autoridad. Si el cliente dice que caminó 50 celdas en 100 ms, el servidor lo rechaza, independientemente de cualquier protección local.
Capa 5 — Handshake firmado con el servidor
Esta es la capa que el atacante no controla, y por eso la más valiosa. En vez de confiar en que el cliente es íntegro, el servidor exige una prueba en cada sesión.
Paso 7 — Desafío-respuesta en la conexión
- Al conectar, el ConnectServer/GameServer envía un nonce aleatorio (valor de uso único).
- El cliente combina ese nonce con el hash de su propio binario y con un secreto incrustado, y responde con un HMAC.
- El servidor recalcula el HMAC con el hash esperado y el mismo secreto. Si no coincide, rechaza la sesión.
Servidor -> Cliente : nonce = 0x9F3A... (aleatorio por sesión)
Cliente -> Servidor: resp = HMAC_SHA256(secreto, nonce || hashBinario)
Servidor : recalcula y compara. Diferente => tumba la conexión.
Como el nonce cambia cada sesión, un atacante no puede grabar una respuesta válida y reproducirla después (anti-replay). Y como el servidor conoce el hashBinario esperado, un Main.exe editado produce un HMAC diferente y queda bloqueado, incluso si el atacante neutralizó el check local.
> El secreto incrustado en el cliente es el eslabón débil: quien haga ingeniería inversa del binario puede extraerlo. Rota el secreto en cada actualización de cliente, combínalo con datos de build y trátalo como algo que eventualmente se filtra. La fuerza real viene de sumar esto a la validación de comportamiento en el servidor.
Capa 6 — Packers y protectores comerciales
Si no tienes el código fuente del cliente, los protectores aplicados al binario ya listo entregan gran parte de las capas 1, 2 y parte de la 4 de una sola vez.
| Herramienta | Enfoque | Contrapartida (ejemplo) |
|---|---|---|
| Themida | Anti-debug, virtualización, integridad | Pesado; falsos positivos de AV; inicio lento |
| VMProtect | Virtualización de tramos críticos | Pérdida de FPS si virtualizas código caliente |
| Enigma Protector | Integridad, licenciamiento, anti-dump | Configuración extensa |
Reglas de oro al usar un protector: virtualiza solo las rutinas críticas (handshake, checks), nunca el loop de render, o los FPS se desploman en máquinas débiles; mantén una build interna sin protección para depurar; y prueba los antivirus antes de lanzar, porque los packers disparan heurística y necesitarás whitelist/reputación.
Errores comunes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| El cliente se cierra al abrir para muchos jugadores | Anti-debug agresivo atrapando overlay/AV | Cambiar el bloqueo por reporte; refinar la allowlist |
| El antivirus borra el Main.exe | Packer sin reputación | Firmar el binario; enviarlo a whitelist; reputación en VirusTotal |
| El hash siempre es inválido en el launcher | Hash de referencia desactualizado tras reedición | Automatizar la regeneración del hash en el build |
| Los FPS cayeron mucho tras proteger | Virtualización del loop de render | Virtualizar solo las rutinas críticas |
| El handshake rechaza clientes legítimos | Reloj/nonce desincronizado o versión equivocada | Verificar la tolerancia de tiempo y AcceptVersion |
| Falso positivo con Discord/Steam | Verificación de módulos ciega | Firmar/allowlist de módulos firmados conocidos |
| El secreto del handshake se filtró | Cadena en texto plano en el binario | Ofuscar/rotar el secreto en cada update |
Distribución y rotación
La protección no es "configura y olvida". Cada nueva versión del cliente debe generar un nuevo hash, un nuevo secreto de handshake y, de ser posible, pequeñas variaciones en las rutinas de check para evitar que una solución pública de bypass valga para siempre. Distribuye siempre por el launcher (que valida antes de correr), publica el hash junto a la descarga y escanea el binario final antes de subirlo. Trata cada bypass reportado en tu Discord como un bug de prioridad alta.
Lista de verificación de lanzamiento
- Copia de seguridad
Main_original.exeguardada fuera de la carpeta del cliente - Hash SHA-256 de referencia generado a partir del binario final
- Verificación de integridad activa en el launcher (hash ofuscado)
- Anti-debug en modo reporte, no en modo kill
- Verificación de módulos con allowlist de firmados conocidos
- Valores sensibles (HP/zen/velocidad) cifrados en el cliente
- Validación server-side de HP, posición, velocidad y paquetes
- Handshake nonce + HMAC activo en el ConnectServer/GameServer
- Secreto del handshake rotable y ofuscado
- Protector aplicado solo en rutinas críticas (FPS probado en PC débil)
- Binario firmado y enviado para reputación de antivirus
- Probado en máquina limpia, separada de la de desarrollo
- Build de debug interna sin protección guardada para depurar
- Plan de rotación de hash/secreto en cada actualización documentado
Preguntas frecuentes
¿El anti-tamper reemplaza al anticheat del servidor?
No. El anti-tamper protege el binario del cliente y la memoria local, mientras que la validación server-side comprueba si las acciones y valores recibidos tienen sentido. Son capas complementarias: un cliente blindado con un servidor ingenuo aún cae por paquetes forjados, y viceversa. Siempre valida en el servidor además de proteger el cliente.
¿Puedo usar Themida o VMProtect en el Main.exe?
Sí, los packers comerciales como Themida, VMProtect y Enigma se usan bastante para dificultar la ingeniería inversa y el debugging. El costo es un mayor consumo de CPU/RAM, un tiempo de inicio más lento y falsos positivos en algunos antivirus. Prueba el rendimiento en máquinas débiles antes de lanzar y mantén una build de debug sin packer para depurar tú mismo.
¿El checksum del cliente puede burlarse?
Un atacante determinado siempre puede neutralizar un check local, porque el código que verifica corre en su máquina. El objetivo del anti-tamper no es ser irrompible, sino elevar el costo del ataque y permitir que el servidor detecte inconsistencias. Por eso el handshake firmado con el servidor, que el atacante no controla, es la parte más valiosa.
¿El anti-debug molestará a los jugadores legítimos?
Puede, si es demasiado agresivo. Técnicas como IsDebuggerPresent y la verificación de ventanas de herramientas raramente afectan a los jugadores, pero cerrar el proceso al detectar cualquier cosa sospechosa genera falsos positivos con overlays, grabadores de pantalla y software de accesibilidad. Prefiere reportar al servidor y dejar la decisión de ban para el análisis, en vez de matar el cliente al instante.
¿Necesito recompilar el Main.exe para agregar protección?
Depende. Si tienes el código fuente del cliente (algo raro), compilas la protección junto. Sin fuente, el camino es aplicar un packer/protector externo al binario y, si hay soporte vía loader o DLL inyectada por tu launcher, agregar checks en ese componente. La verificación de integridad y el handshake pueden vivir en el launcher y en el ConnectServer sin tocar el Main.exe.