Getting Started
Wompfuscator protects your JVM applications with multiple layers of obfuscation and security. This guide will get you up and running in under 5 minutes.
1. Create a Configuration File
Create a config.yml in your project directory. This file will hold all of your
obfuscation settings, targets, and exemptions.
2. Run Wompfuscator
java -jar wompfuscator.jar --config config.yml
That's it! Your protected JAR will be written to the output path.
Configuration
All settings are controlled through a single config.yml file.
| Field | Type | Description |
|---|---|---|
seed |
String | Optional Random seed for reproducible builds |
input |
Path | Required Path to your compiled JAR file |
output |
Path | Required Where to write the protected JAR |
target-jdk |
Path | Optional Path to the JDK installation (Required only if using Native Loader) |
libraries |
List | Optional Dependency JARs your application needs to resolve types |
exempt |
List | Optional Packages/classes to exclude from obfuscation |
targets |
List | Optional Native compilation targets (e.g. x86_64-windows). Defaults to your current OS if
Native Loader is enabled. |
Security Tips
Follow these best practices to get the most out of Wompfuscator's protection.
Keep Secrets on the Stack
Never store sensitive keys as class fields. Fields are easily visible in heap dumps and through reflection. Keep them as local variables inside protected methods.
public class APIConfig { public static String TOKEN = "sk-live-12345"; public void init() { connect(TOKEN); } }
public class APIConfig { public void init() { String token = "sk-live-12345"; connect(token); } }
Combine Multiple Layers
Use the Packer together with Anti-Dump and Anti-Agent for maximum effect. The Packer encrypts your classes, Anti-Dump prevents memory extraction and Anti-Agent blocks debugging tools.
Native Loader
The Native Loader compiles your Java bytecode into a native binary that is loaded at runtime. The logical instructions are completely removed from the JVM which prevents decompilers from seeing how the method works.
public class LicenseManager { public boolean verifySignature(String key) { if (key == null) return false; return Crypto.checkRSA(key, "PUB_KEY"); } }
public class LicenseManager { // Logic compiled to native C++ binary // JVM has no knowledge of the implementation public native boolean verifySignature(String key); static { System.loadLibrary("core_x86_64"); } }
Supported Platforms
| Target | OS | Architecture | Output |
|---|---|---|---|
x86_64-windows |
Windows | x86_64 | .dll |
x86_64-linux |
Linux | x86_64 | .so |
aarch64-linux |
Linux | ARM64 | .so |
x86_64-macos |
macOS | Intel | .dylib |
aarch64-macos |
macOS | Apple Silicon | .dylib |
Packer
The Packer wraps your classes into an encrypted payload. Classes are loaded directly into memory at runtime to bypass the file system entirely.
For frameworks like Bukkit, Fabric, or Spigot, the Packer automatically generates specialized classes to ensure seamless integration:
- Bootstrap Class: (e.g.,
Bootstrap) Initializes the native engine and decrypts the payload directly into JVM memory before the application actually starts. - EntryPoint Stub: A proxy class that mimics your original main class. It tricks the mod/plugin loader into loading the application normally, and immediately hands off execution to your real, decrypted code.
📁 my-plugin-v1.0.jar ├── Main.class ├── ConfigHandler.class ├── EventListener.class ├── CommandManager.class └── UserSession.class
📁 my-plugin-protected.jar ├── Bootstrap.class ├── Entrypoint.class ├── classes.vm ← Encrypted classes └── win32.dll ← Native Engine
String Encryption
All string literals in your code are encrypted in the bytecode and only decrypted when needed at runtime. This prevents anyone from searching the JAR for sensitive text.
public void connectDatabase() { Logger.info("Connecting to db.local:5432"); db.login("admin", "super_secret_password"); }
public void O0OO0() { // Poly2 uses ConstantDynamic for decryption // Decompilers can't represent this in Java a.info((String) /* dynamic */ O00O0O( "x\u001A\u0099+", 0x7F2A91BC, 42 )); b.login( (String) O00O0O("\u0011\"", 0x1A, 1), (String) O00O0O("3D", 0x2B, 2) ); }
Control Flow Obfuscation
Transforms the logical structure of your code into a complex maze. Your application runs exactly the same but decompilers produce unreadable output.
public void processUser(User u) { if (u.isAdmin()) { grantAdminAccess(); } else { showErrorMenu(); } }
public void a(User var1) { int var2 = 148923; while (var2 != 0) { switch (var2) { case 148923: var2 = var1.isAdmin() ? 892341 : 558129; break; case 892341: this.grantAdminAccess(); var2 = 0; break; case 558129: this.showErrorMenu(); var2 = 0; break; } } }
Number Encryption
Numeric constants are replaced with complex mathematical expressions using Mixed Boolean Arithmetic (MBA). Simple integers become unreadable formulas extracted into hidden arrays and evaluated at runtime.
public void healPlayer(Player p) { p.updateStatus(20); p.applyShield(100); }
private static int[] O0OO0; // Hidden lookup table public void O00O0(Player var1) { // Numbers extracted to arrays with MBA indexing var1.O0OOO(O0OO0[ ((150 ^ 83) & ~(150 | 83)) + 4 ]); var1.O0O0O(O0OO0[ ~(~7 | 3) + (12 ^ 5) ]); }
Name Obfuscation
All meaningful names are replaced with short randomized identifiers. This removes all context and makes the code unrecognizable.
public class NetworkManager { private String serverIp; public void sendPacket(byte[] payload) { socket.write(payload); } }
public class O0OO00 { private String o0o0o0; public void O0O0O(byte[] var1) { O000OO.O0O(var1); } }
Array Obfuscation
Array allocations and length checks are rewritten to use the Java Reflection API. This breaks decompilers that attempt to reconstruct array initializations.
public void allocateCache() { String[] cache = new String[10]; int size = cache.length; }
public void O0OO0() { // newarray and arraylength → reflection String[] var1 = (String[]) Array.newInstance( String.class, 10 ); int var2 = Array.getLength(var1); }
Reference Proxying
Hides the relationship between your code and the APIs it calls. Method invocations and field accesses are routed through dynamically generated synthetic proxies and InvokeDynamic instructions.
public void logEvent(String msg) { System.out.println(msg); }
public void O00O0(String var1) { // Calls routed through synthetic bridges O0OO00.O00O( O000O.O0(), var1 ); }
Decompiler Crashers
Specially crafted bytecode sequences that exploit known bugs in decompilers. When someone tries to decompile your JAR the decompiler will crash, freeze or produce broken output.
Targeted Decompilers
- CFR — Stack overflows and infinite loops
- Procyon — Invalid AST generation
- Vineflower / Fernflower — Crashes on malformed attributes
- JD-GUI — UI freeze on specially crafted code
// CFR crashes with StackOverflowError: java.lang.StackOverflowError at org.benf.cfr.reader.bytecode.analysis... ... 1024 more // Procyon output: ERROR: Failed to decompile class 'a/b/c' ERROR: Unexpected opcode at offset 0x2F
Integrity Checks
Runtime integrity verification detects if your code has been tampered with. Includes three protection layers:
Anti-Tamper
Injects checksum verification into critical classes. If the bytecode has been modified the application detects the change and takes protective action.
Anti-Dump
Prevents attackers from extracting decrypted classes from JVM memory. Works by monitoring class redefinition and detecting debugger attachment.
Anti-Agent
Blocks Java agents from attaching to your running application. This prevents tools from hooking into your code.
Virtualization
Converts your Java bytecode into custom opcodes that run on a built-in virtual machine. This is the strongest protection layer because the attacker needs to fully reverse-engineer a custom instruction set.
public boolean hasPermission(int level) { return level >= 50; }
public boolean a(int var1) { // Translated to custom VM opcodes return (boolean) WompVM.execute( new int[]{ 0xFA, 0x11, 0x99, 0x2B, 0x4C, 0x88, 0x10, 0x00 }, new Object[]{var1} ); }
Exemptions
Exclude specific packages, classes, methods or fields from obfuscation. This is essential for API
compatibility, reflection targets and framework entry points. You can also specify
skip-heavy paths to bypass extreme transformations on performance-sensitive hotpaths.
Resource Encryption
Encrypt config files, JSON, XML and properties files embedded in your JAR. They are decrypted transparently at runtime so your code doesn't need any changes.
Platform Support
Wompfuscator has first-class support for Minecraft modding and plugin frameworks:
| Platform | Support | Notes |
|---|---|---|
| Fabric | Full | Mixin-aware obfuscation via Fabric-Protect |
| Bukkit | Full | Preserves event handlers and command APIs |
| Paper | Full | Paper-specific APIs and async features preserved |
| Spigot | Full | Compatible with Spigot plugin conventions |
| Velocity | Full | Proxy plugin obfuscation with event preservation |
| Standalone | Full | Any JVM application |