rymga ← Back to Lock Master

Class encryption

Everything else makes your bytecode hard to read. ClassGuard removes it from disk entirely: your classes ship as encrypted blobs with no loadable bytecode to decompile, unsealed only in memory, only at load time. It's the strongest single option in Lock Master.

What it does

With classGuard on, each of your classes is encrypted with strong authenticated encryption and the original .class file is stripped from the jar. In its place ships an opaque encrypted blob. A tiny generated bootstrap loader — the only real bytecode left — reconstructs the key at runtime and unseals each class on demand, in memory. Nothing decrypted ever touches the disk.

{ "classGuard": { "enabled": true, "mode": "STANDALONE" } }

The result: a decompiler opening your jar finds almost nothing to work with. No class names, no methods, no strings — just ciphertext and a loader.

Before & after

Real output. A six-class jar, before and after ClassGuard. Every business class is gone from the file listing — replaced by encrypted .bin blobs under META-INF/guardian/ — and only the two-class bootstrap loader remains as openable bytecode:

Your jar — 6 classes
demo/Heavy.class
demo/Report.class
demo/Sample.class
shop/App.class
shop/model/Cart.class
shop/util/Money.class
After — sealed
a/a.class
b/b.class
META-INF/guardian/-4512027295375453326.bin
META-INF/guardian/5166365122521541383.bin
META-INF/guardian/-5206015750035504180.bin
META-INF/guardian/-5405373286271541278.bin
META-INF/guardian/6344447860271584778.bin
META-INF/guardian/7602776200188678415.bin

The .bin files are high-entropy ciphertext — no readable strings, no class structure, nothing a decompiler can parse. Your Sample, Report, Cart and the rest simply aren't there as bytecode any more. To read them, an attacker has to defeat the encryption and capture the classes as the JVM unseals them at runtime — which is the path antiDebug closes.

Modes & fields

ClassGuard needs an entry point where it can unseal your classes before anything uses them. That's the only real decision: where your program starts.

FieldWhat it does
enabledTurn on whole-class encryption.
modeSTANDALONE — for normal apps: a generated launcher class becomes the main entry point and unseals everything on startup. PLUGIN — for Minecraft plugins (Bukkit / Spigot / Paper): Lock Master renames and encrypts your real main and ships a small cleartext stub under your plugin.yml main name; the server loads the stub, which boots the loader and hands off to your real main. Rename must be on.
pluginMainClassRequired for PLUGIN mode. The internal name of your plugin's main class (matching plugin.yml).
excludeClassesClasses or prefixes to leave unencrypted — anything loaded very early, or by name, before the loader is ready. In PLUGIN mode you don't list your main here: it's handled for you.
Plugin authors — don't keep your main. In PLUGIN mode you set pluginMainClass and that's it: don't add your main to keep or excludeClasses. Lock Master renames and encrypts your real main and ships a cleartext stub under your plugin.yml name for the server to load, so the main must be renamed (keeping it is rejected). This differs from plain renaming without ClassGuard, where you do keep the main because plugin.yml isn't rewritten.

antiDebug

Sealing the classes closes static analysis, so the remaining attack is dynamic: run the program under a debugger and dump the classes as they're unsealed. antiDebug closes that door. At a ClassGuard entry point it inspects the JVM's launch configuration and, if a debugger is attached, refuses — so the encrypted classes are never unsealed under observation.

FieldWhat it does
enabledTurn it on. Needs ClassGuard to have an entry point.
action"throw" — refuse to unseal (fail to enable/load). "exit" — kill the JVM outright.
checkAgentsAlso flag -javaagent / -agentpath. Off by default — many legitimate servers run monitoring or profiling agents, so only enable it if yours doesn't.

watermark

An invisible, recoverable identifier stamped redundantly across a jar, so a leaked copy traces back to whoever it was handed to. It's split and checksummed across classes, so it survives re-jarring or partial deletion, and reading it back needs a secret that never ships in the build.

It's a CLI command, not a config switch. You stamp a per-buyer id into an already-obfuscated jar with rymga-cli watermark stamp, and recover it from a leak with watermark trace — both run server-side. See Per-buyer watermark.

Before you ship

  • Test on a real run. ClassGuard changes how your program loads — always run the sealed build on a real server or launch before shipping.
  • Works alongside other plugins. The guard now loads its own encrypted classes child-first, so two plugins whose obfuscated classes end up with the same short name no longer collide on a shared server — the failure that used to surface as a ClassNotFoundException / NoSuchMethodError when another plugin was installed. Async scheduler tasks and threads your own code starts resolve encrypted classes correctly too.
  • One edge remains: name-based loading from a foreign thread. A bundled library that loads a class by name off a thread it started itself, using that thread's context class loader rather than its own (some builds of bStats do this), can still miss an encrypted class. If a sealed build throws ClassNotFoundException from inside such a library, list it in excludeClasses, or skip ClassGuard for that jar — rename + string encryption + flow still protect strongly. See the real-plugin example.
  • It's non-reproducible by design. The encryption key is drawn from a secure random source, so a ClassGuard build differs every time even with a fixed seed. That's deliberate — a reproducible key would defeat the purpose.
  • Mind the entry point. Keep whatever starts your program (or is loaded before the loader runs) in excludeClasses.