简体中文 | English
A powerful Java bytecode protection tool that converts Java methods into native code execution, providing strong protection against decompilation and reverse engineering.
- Native VM Execution: Converts Java bytecode to native code executed by a custom VM interpreter
- Opcode Obfuscation: Each bytecode instruction is mapped to a random opcode value, preventing easy reconstruction
- ChaCha20 Encryption: All bytecode and strings are encrypted using ChaCha20 stream cipher
- Cross-Platform: Supports multiple targets via Zig compiler (Windows, Linux, macOS, Android)
- INVOKEDYNAMIC Support: Support for lambda expressions and dynamic method invocation (experimental)
Warning: INVOKEDYNAMIC implementation is experimental and may contain bugs. Use with caution in production environments.
- Java 8+
- Zig 0.11+ (for native compilation)
- Gradle 8+
Create a config.yml file:
# Input JAR file
jar: test/app.jar
# Output JAR file
out: test/app-protected.jar
# Protection rules
protect:
- "**" # Protect all methods
# Target platforms
targets:
- x86_64-windows-gnu
# Options
debug: false
native-dir: native
protect-bootstrap-payload: false
direct-native-rewrite: falsejava -jar jnvm.jar config.yml| Rule | Description |
|---|---|
package.** |
Protect all methods in package and subpackages |
ClassName |
Protect all methods in class |
ClassName#methodName |
Protect specific method |
@annotation |
Protect methods with annotation |
direct-native-rewrite:
false(default): Protect by rewriting method body to call bridge.true: Rewrite protected non-<clinit>methods to realnative, and register per-class natives in that class<clinit>.
package pack.tests.reflects.annot;
import java.lang.reflect.Field;
import pack.tests.reflects.annot.anno;
public class annoe {
@anno(val="PASS")
private static final String fail = "WHAT";
@anno
public void dox() throws Exception {
String toGet = "FAIL";
for (Field f : annoe.class.getDeclaredFields()) {
f.setAccessible(true);
anno obj = f.getAnnotation(anno.class);
if (obj == null) continue;
toGet = obj.val();
}
System.out.println(toGet);
}
@anno(val="no")
public void dov() {
System.out.println("FAIL");
}
}package pack.tests.reflects.annot;
import lib.xml.abc.Dispatcher;
import pack.tests.reflects.annot.anno;
public class annoe {
@anno(val="PASS")
private static final String fail = "WHAT";
@anno
public void dox() throws Exception {
Dispatcher.executeVoid(1282844577, new Object[]{this, annoe.class});
}
@anno(val="no")
public void dov() {
Dispatcher.executeVoid(1282844576, new Object[]{this, annoe.class});
}
}The original method bodies are replaced with native VM calls, making reverse engineering extremely difficult.
- Scanning: Analyzes the input JAR to find methods matching protection rules
- Encryption: Encrypts bytecode using ChaCha20 with unique keys per method
- Code Generation: Generates C source files:
vm_types.h- VM type definitionsvm_data.c- Encrypted method data and string poolvm_interpreter.c- Custom bytecode interpretervm_bridge.c- JNI bridge with RegisterNatives
- Compilation: Compiles native code using Zig for specified targets
- Patching: Rewrites protected methods to call the native VM
- Packaging: Embeds native libraries into the output JAR
JNVM has been tested with JARs obfuscated by:
- Zelix KlassMaster (ZKM) - support including encrypted string decryption , invokedynamic
- ProGuard - Standard obfuscation
- Allatori - String encryption and flow obfuscation
- Vanilla Java - No obfuscation
NOP, ACONST_NULL, ICONST_M1 to ICONST_5, LCONST_0, LCONST_1, FCONST_0 to FCONST_2, DCONST_0, DCONST_1, BIPUSH, SIPUSH, LDC, LDC_W, LDC2_W
ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ILOAD_0 to ILOAD_3, LLOAD_0 to LLOAD_3, FLOAD_0 to FLOAD_3, DLOAD_0 to DLOAD_3, ALOAD_0 to ALOAD_3, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE, ISTORE_0 to ISTORE_3, LSTORE_0 to LSTORE_3, FSTORE_0 to FSTORE_3, DSTORE_0 to DSTORE_3, ASTORE_0 to ASTORE_3
POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP
- Add/Sub/Mul/Div/Rem:
IADD,LADD,FADD,DADD,ISUB,LSUB,FSUB,DSUB,IMUL,LMUL,FMUL,DMUL,IDIV,LDIV,FDIV,DDIV,IREM,LREM,FREM,DREM - Negation:
INEG,LNEG,FNEG,DNEG - Bitwise/Shift:
ISHL,LSHL,ISHR,LSHR,IUSHR,LUSHR,IAND,LAND,IOR,LOR,IXOR,LXOR - Conversion:
I2L,I2F,I2D,L2I,L2F,L2D,F2I,F2L,F2D,D2I,D2L,D2F,I2B,I2C,I2S - Comparison:
LCMP,FCMPL,FCMPG,DCMPL,DCMPG
- Conditional:
IFEQ,IFNE,IFLT,IFGE,IFGT,IFLE,IF_ICMPEQ,IF_ICMPNE,IF_ICMPLT,IF_ICMPGE,IF_ICMPGT,IF_ICMPLE,IF_ACMPEQ,IF_ACMPNE,IFNULL,IFNONNULL - Unconditional:
GOTO,GOTO_W - Switch:
TABLESWITCH,LOOKUPSWITCH - Return:
RETURN,IRETURN,LRETURN,FRETURN,DRETURN,ARETURN - Other:
IINC,ATHROW
NEW, CHECKCAST, INSTANCEOF, GETFIELD, PUTFIELD, GETSTATIC, PUTSTATIC, INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, INVOKEDYNAMIC
NEWARRAY, ANEWARRAY, MULTIANEWARRAY, ARRAYLENGTH, IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE
MONITORENTER, MONITOREXIT
| Opcode | Reason |
|---|---|
JSR, RET, JSR_W |
Deprecated since Java 6 |
WIDE |
Extended local variable indexing (rarely needed) |
RET_* variants |
Part of WIDE extension |
git clone https://github.com/AlphaAutoLeak/jnvm.git
cd JNVM
./gradlew buildThe compiled JAR will be in build/libs/.
This project is provided for educational and legitimate software protection purposes only.
Use this tool responsibly. The authors are not responsible for any misuse or damage caused by this software.