Cifrado de clases
El resto de las técnicas hacen que tu bytecode sea difícil de leer. ClassGuard lo elimina por completo del disco: las clases se distribuyen como bloques cifrados sin bytecode cargable que descompilar y solo se desprecintan en memoria al cargarlas. Es la opción más potente de Lock Master.
Qué hace
Con classGuard activado, cada clase seleccionada se cifra mediante cifrado autenticado robusto y se elimina del JAR el archivo .class original. En su lugar se distribuye un bloque cifrado opaco. Un único cargador generado —el guardián, el único bytecode adicional— reconstruye la clave durante la ejecución y desprecinta cada clase bajo demanda y en memoria. Ningún dato descifrado llega al disco.
Todas las clases selladas se agrupan en un único conjunto comprimido y autenticado
(META-INF/guardian/pool.bin): se comprime y cifra una sola vez, de modo que comparte un diccionario y una única etiqueta en vez de añadir sobrecarga por clase. El resultado cifrado es bastante menor que al sellar cada clase por separado, algo especialmente importante en JAR con muchas clases pequeñas.
{ "classGuard": { "enabled": true, "encrypt": ["com/you/internal/"] } }
No hay varios modos : solo un modelo selectivo. Indicas qué quieres cifrar with
encrypt; todo lo que omitas seguirá siendo bytecode normal en claro y el entorno lo cargará de la forma habitual. En las clases selladas, un descompilador apenas encontrará material con el que trabajar: no habrá nombres de clases, métodos ni cadenas, solo texto cifrado y un cargador.
Antes y después
Resultado real de un JAR de seis clases con todo seleccionado para cifrar, antes y después de ClassGuard. Todas las clases de negocio desaparecen del listado de archivos y quedan integradas en el único conjunto cifrado ubicado en
META-INF/guardian/pool.bin ; solo el cargador guardián —además de cualquier clase que hayas dejado en claro— permanece como bytecode que se puede abrir:
demo/Heavy.class
demo/Report.class
demo/Sample.class
shop/App.class
shop/model/Cart.class
shop/util/Money.class
a/a.class
b/b.class
META-INF/guardian/pool.bin
El archivo pool.bin contiene texto cifrado de alta entropía: no hay cadenas legibles, estructura de clases ni nada que pueda analizar un descompilador. Tus clases Sample,
Report, Cart y las demás ya no existen como bytecode. Para leerlas, un atacante debe vulnerar el cifrado y capturar las clases cuando la JVM las desprecinta durante la ejecución; esa es precisamente la vía que cierra anti-dump
—junto con la protección más ligera antiDebug—.
Qué cifrar
Un solo campo contiene toda la decisión: encrypt, una lista de inclusión con lo que se sellará. Todo lo que no aparezca en ella seguirá en claro y se cargará normalmente.
| Campo | Qué hace |
|---|---|
| enabled | Activa el cifrado de clases completas. |
| encrypt | Nombres internos (com/you/Secret) o prefijos de paquetes (com/you/internal/) de las clases que se sellarán. "*" sella todo —salvo lo incluido en keep—. También se sella todo si se omite o se deja vacío. Una lista concreta sella solo las clases sensibles que indiques; esta es la configuración recomendada. |
La lista admite tantas clases y paquetes como quieras, combinados libremente : un nombre sin más (com/you/licensing/KeyCheck) representa una clase y sus clases internas; una barra final (com/you/internal/) representa todo un árbol de paquetes. Puedes combinar clases individuales y varios paquetes:
{
"classGuard": {
"enabled": true,
"encrypt": [
"com/you/licensing/KeyCheck",
"com/you/internal/",
"com/you/crypto/"
]
}
}
encrypt respeta keep: todo lo incluido en keep nunca se sella, ni siquiera si coincide con "*". Por eso, el patrón habitual consiste en usar encrypt: ["*"] junto con una lista keep para los puntos de entrada, o bien una lista encrypt estricta que solo indique las clases que merece la pena ocultar. Es el mismo patrón de coincidencia que utilizan rename.exclude,
flow.exclude y
stringEncryptionExclude.
La regla de enlace
Una clase en claro no puede hacer referencia por nombre a una clase sellada de forma directa: durante el enlazado, la JVM intentaría resolver una clase que no existe como bytecode. Lock Master enlaza automáticamente el único patrón seguro: una clase sellada utilizada mediante una interfaz o superclase en claro —la típica «implementación sensible detrás de una interfaz limpia»—. Una instrucción new Secret() se convierte en una creación reflectiva mediante el guardián y el resultado se convierte de nuevo a esa interfaz; no tienes que escribir nada.
Cualquier otra referencia directa desde código en claro a una clase sellada —un campo de ese tipo, una conversión, una llamada estática, Secret.classo una clase que la extienda— hace que la compilación falle de inmediato con un mensaje que identifica ambas clases y explica la solución: sellar también esa clase o pasar por una interfaz en claro. Nunca se genera un JAR roto. En la práctica, cifra unidades cohesionadas: un paquete o una implementación sin ningún elemento externo que acceda a ella por nombre.
Patrones arquitectónicos
Debido a este límite del ClassLoader, una clase en claro —como Main, que debe permanecer en claro para que la JVM anfitriona pueda cargarla— no puede contener un campo, una conversión ni una instrucción new de una clase concreta cifrada. Por sí solo, ClassGuard solo enlaza el patrón seguro —una clase sellada detrás de una interfaz o superclase en claro— y detiene la compilación en los demás casos. Tendrías que escribir a mano una interfaz para cada clase interna a la que quisieras hacer referencia:
// Without type decoupling, this fails the build:
public class Main {
private ConfigService configService; // hard ref to an encrypted concrete class
public void onEnable() { this.configService = new ConfigService(); }
}
// …you'd have to refactor everything behind a cleartext interface by hand:
public class Main {
private IService configService; // OK: cleartext interface
public void onEnable() { this.configService = new ConfigService(); }
}
typeDecoupling
y no tendrás que refactorizar nada: el motor extrae la interfaz en claro, canaliza la creación mediante una factoría generada y reescribe automáticamente todas las referencias. Tu código sigue nombrando tipos concretos y el ofuscador permite sellarlos. Es la forma recomendada de usar ClassGuard en un proyecto real y permite pasar de sellar «unas pocas interfaces» a la mayoría de las clases.Puntos de entrada: nunca selles lo que el entorno de ejecución busca por nombre
Este es un ofuscador de Java de uso general, no una herramienta exclusiva para plugins, y la regla es siempre la misma: cualquier clase que el entorno de ejecución localice por su nombre debe permanecer en claro con ese nombre. Si está cifrada, quien la busque no encontrará bytecode cargable y el JAR no arrancará. Esto incluye:
- la clase
Main-Classde un JAR ejecutable (java -jar); - la clase principal de un plugin o mod que el anfitrión busca en un descriptor (
plugin.ymly similares); ServiceLoaderlos proveedores enumerados enMETA-INF/services;- cualquier elemento que un framework o tu propio código cargue por reflexión mediante una cadena —beans de Spring, drivers JDBC,
Class.forName(...)).
Lock Master se ocupa de los casos habituales: detecta el Main-Class, el campo mainde todos los descriptores de plugins y los ServiceLoader proveedores situados en
META-INF/services, y los mantiene en claro automáticamente, incluso si coinciden con
encrypt: ["*"]. Lo que no puede detectar es una clase que cargas por reflexión a partir de una
cadena creada por tu código; deja esas clases fuera de encrypt —esa llamada depende de ti—. Lo mismo se aplica al renombrado: no renombres una clase que el entorno resuelva mediante un nombre que no pueda sincronizarse. Sin embargo, excluir una clase del sellado y el renombrado
no la deja desprotegida : precisamente por eso cada técnica tiene sus propias exclusiones. El punto de entrada sigue recibiendo cifrado de cadenas y ofuscación del flujo de control; simplemente conserva su nombre y sigue siendo cargable. Protégelo por dentro, no se lo ocultes al cargador.
encrypt— ni lo renombres (rename.exclude); la ofuscación del flujo y el cifrado de cadenas seguirán aplicándose. Usa
keep solo para una API pública cuyos nombres deban conservarse por completo.Protección durante la ejecución: antiDebug y anti-dump
Sellar las clases impide el análisis estático, por lo que solo queda el ataque dinámico: ejecutar el programa con un depurador y volcar las clases al desprecintarlas. Dos opciones cierran esa vía con distinta intensidad.
antiDebug es la opción ligera : una única comprobación en el punto de entrada examina la configuración de arranque de la JVM y se niega a iniciar si hay un depurador conectado. Así, las clases cifradas nunca se desprecintan bajo observación. Es un bloque de nivel superior y funciona incluso sin ClassGuard.
| Campo | Qué hace |
|---|---|
| enabled | Activa la función. |
| action | "throw" — se niega a desprecintar —falla la activación o la carga—. "exit" — finaliza la JVM de inmediato. |
| checkAgents | Detecta también -javaagent / -agentpath. Desactivado de forma predeterminada : muchos servidores legítimos ejecutan agentes de monitorización o perfilado; actívalo solo si el tuyo no lo hace. |
classGuard.antiDump ofrece una protección durante la ejecución mucho más potente y nativa de ClassGuard: varias capas independientes —detección nativa de depuradores, refuerzo anti-dump en el sistema operativo, bibliotecas nativas con detección de manipulaciones y mecanismos de detección dispersos—. La diferencia clave es que la detección se integra en la clave de descifrado en vez de depender de una única comprobación que pueda eliminarse con un parche. Bajo un depurador, la clave sencillamente resulta incorrecta y nada se descifra. Tiene su
propia página, donde se explica una salvedad importante: deniega ante cualquier fallo provocado por cualquier agente —incluidos los perfiladores—.watermark
Un identificador invisible y recuperable se graba de forma redundante por todo el JAR para atribuir una copia filtrada a quien la recibió. Se divide entre varias clases con suma de comprobación, por lo que resiste el reempaquetado o un borrado parcial; para leerlo se necesita un secreto que nunca se distribuye con la compilación.
rymga-cli watermark stamp, y recupéralo de una filtración mediante watermark trace ; ambos comandos se ejecutan en el servidor. Consulta
Marca de agua por comprador.Antes de publicar
- Pruébalo en una ejecución real. ClassGuard cambia la forma de cargar el programa. Ejecuta siempre la compilación sellada en un servidor real o inicia la aplicación antes de distribuirla.
- Funciona junto con otros plugins. El guardián carga ahora sus propias clases cifradas
primero desde el cargador hijo. Así, dos plugins cuyas clases ofuscadas terminen con el mismo nombre corto ya no entran en conflicto en un servidor compartido; ese fallo antes aparecía como
ClassNotFoundException/NoSuchMethodErroral instalar otro plugin. Las tareas asíncronas del planificador y los hilos iniciados por tu código también resuelven correctamente las clases cifradas. - Queda un caso límite: la carga por nombre desde un hilo ajeno. Una biblioteca incluida que cargue una clase por nombre desde un hilo que ella misma ha iniciado y use el cargador de clases de contexto de ese hilo en vez del suyo —algunas compilaciones de bStats lo hacen— todavía puede no encontrar una clase cifrada. Si una compilación sellada lanza
ClassNotFoundExceptiondesde una biblioteca de este tipo, deja el paquete de la biblioteca fuera deencrypt—o añádelo a keep—, o no uses ClassGuard para ese JAR. El renombrado, el cifrado de cadenas y el flujo siguen ofreciendo una protección sólida. Consulta el ejemplo de un plugin real. - No es reproducible por diseño. La clave de cifrado procede de una fuente aleatoria segura, por lo que cada compilación de ClassGuard es distinta incluso con una seed fija. Es intencionado: una clave reproducible frustraría el propósito de la protección.
- Presta atención al punto de entrada. Deja fuera cualquier elemento que el entorno encuentre por nombre —la clase
Main-Class, la clase principal del plugin o los proveedores de servicios— fueraencrypty sin renombrar. La ofuscación del flujo y el cifrado de cadenas seguirán protegiéndolo.