St3ix Obfuscator
Actively developed

St3ix Obfuscator

A Java bytecode obfuscator that transforms JAR files to make reverse engineering harder. Class names, strings, numbers, and more—all while preserving runtime behavior.

Quick Start

# Build the obfuscator
./gradlew dist

# Obfuscate a JAR
java -jar build/dist/st3ix-obfuscator-v*.jar -i myapp.jar -o myapp-obfuscated.jar

# Open GUI (Linux, macOS, or if you want to keep it simple)
java -jar build/dist/st3ix-obfuscator-v*.jar

Requirements: Java 17+, Gradle (wrapper included). Windows: use gradlew.bat and run.bat or java -jar for the GUI.

Before & After

Decompiled view: readable source becomes unreadable after obfuscation. Class names, methods, fields, numbers, strings, and control flow are transformed.

Flow obfuscation, numbers & strings

Before (original source)

package example;

public final class ObfuscationVerifyDemo {

    private static final String apiKey = "verify-field-renaming";

    public static void main(String[] args) {
        ObfuscationVerifyDemo demo = new ObfuscationVerifyDemo();
        demo.verify();
    }

    public void verify() {
        int port = 25565;
        int seed = 12345;
        int threshold = 1000;
        int sum = port + seed + threshold;

        System.out.println("ObfuscationVerifyDemo: port=" + port + ", seed=" + seed + ", threshold=" + threshold);
        System.out.println("Sum=" + sum);
        System.out.println("Field (apiKey): " + apiKey);
    }
}

After (decompiled output)

import java.nio.charset.StandardCharsets;

public final class i\u202ffpdnbunzqoaxx {
    private static final String rokb\u200c\u0435eg\u0441\u0445ldl;

    public static void main(String[] stringArray) {
        int n = 999980078;
        block5: while (true) {
            switch (n ^ 0x3B9A7C2E) {
                case 0: {
                    i\u202ffpdnbunzqoaxx i2 = new i\u202ffpdnbunzqoaxx();
                    n = 999980079;
                    continue block5;
                }
                case 1: {
                    i2.\u043euxl\u200bbvwtg\u043f\u0430wnk();
                    n = 999980076;
                    continue block5;
                }
                case 2: return;
            }
        }
    }

    public void \u043euxl\u200bbvwtg\u043f\u0430wnk() {
        int n = (6391 << 2) + (0x811D228E ^ 0x811D228F);
        int n2 = (6172 << (0x811D228E ^ 0x811D228F)) + (0x811D228E ^ 0x811D228F);
        int n3 = 637 + 363;
        System.out.println("ObfuscationVerifyDemo: port=" + n + ", seed=" + n2 + ", threshold=" + n3);
        byte[] byArray = new byte[]{192, 238, 237, ...};
        int n5 = i\u202ffpdnbunzqoaxx.class.getName().hashCode() ^ 0xD30F1B4C;
        for (int i = 0; i < byArray.length; ++i) {
            byArray2[i] = (byte)(byArray[i] ^ n5 + i & 0xFF);
        }
        System.out.println(new String(byArray2, StandardCharsets.UTF_8));
    }

    static {
        byte[] byArray = new byte[]{240, 226, 250, ...};
        rokb\u200c\u0435eg\u0441\u0445ldl = new String(byArray2, StandardCharsets.UTF_8);
    }
}

Flow obfuscation (switch/case), number obfuscation (25565→expr, 12345→expr, 1000→expr), string XOR, homoglyph/invisible chars.

Cross-class references

Before (original source)

package example;

import example.model.User;
import example.util.StringHelper;

public final class CrossRefTest {

    private final DemoService service;
    private final User user;

    public CrossRefTest() {
        this.service = new DemoService();
        this.user = new User("test-user", 42);
    }

    public void run() {
        service.run();
        String reversed = StringHelper.reverse("cross-ref");
        System.out.println("Reversed: " + reversed);
        String formatted = StringHelper.formatIdLinear(user.getId());
        System.out.println("Formatted: " + formatted);
        System.out.println("User: " + user);
    }
}

After (decompiled output)

import java.nio.charset.StandardCharsets;

public final class qm\u200c\u0443rjzo\u0441ub\u0441ijf {
    private final nb\u200dxmhknidyfjnv \u200ad\u043ermtgvh\u0441\u0445zz = new nb\u200dxmhknidyfjnv();
    private final yaghdirkvtrn\u0443g\u200d \u2060vk\u0445hoqoubjm\u0445;

    public qm\u200c\u0443rjzo\u0441ub\u0441ijf() {
        byte[] byArray = new byte[]{106, 122, 83, 85, 15, 86, 87, 64, 84};
        int n = qm\u200c\u0443rjzo\u0441ub\u0441ijf.class.getName().hashCode() ^ 0xD30F1B4C;
        byte[] byArray2 = new byte[byArray.length];
        for (int i = 0; i < byArray.length; ++i) {
            byArray2[i] = (byte)(byArray[i] ^ n + i & 0xFF);
        }
        this.\u2060vk\u0445hoqoubjm\u0445 = new yaghdirkvtrn\u0443g\u200d(new String(byArray2, StandardCharsets.UTF_8), 123 - 81);
    }

    public void \u0441qvfujvngavlad\u200b() {
        this.\u200ad\u043ermtgvh\u0441\u0445zz.m\u0435s\u0435h\u200ebbh\u0441c\u0441gcj();
        byte[] byArray = new byte[]{125, 109, 79, 82, 81, 14, 86, 64, 64};
        String string = ag\u0430kdkeuorhfzr\u200a.ignxmtpme\u200bsgp\u0430b(new String(byArray2, StandardCharsets.UTF_8));
        System.out.println("Reversed: " + string);
        String string2 = ag\u0430kdkeuorhfzr\u200a.\u043f\u043fiulqyhzitrf\u200bh(this.\u2060vk\u0445hoqoubjm\u0445.sdibe\u200blvrnochxt());
        System.out.println("Formatted: " + string2);
        System.out.println("User: " + String.valueOf(this.\u2060vk\u0445hoqoubjm\u0445));
    }
}

Class renaming (CrossRefTest→qm..., DemoService→nb..., User→yag...), method renaming (run→...), string obfuscation, homoglyphs in identifiers.

Features

StatusFeatureDescription
Class renamingShort or random names, configurable length
Method renamingHandles override chains; excludes main, constructors, native
Field renamingExcludes serialVersionUID and enum constants
Homoglyph & invisible charsUnicode lookalikes and zero-width chars in names
Number obfuscationint via math expressions; long/float/double via XOR
Array obfuscationHides array dimensions (XOR)
Boolean obfuscationHides true/false literals (XOR)
String obfuscationXOR encryption; inline decrypt; static final fields in <clinit>
Debug info strippingRemoves source names, line numbers, local vars
GUIGraphical interface (java -jar or run.bat)
YAML configconfig.yml next to the JAR
Flow obfuscationControl flow transformations
Mapping file outputPlanned

✓ = Implemented    ○ = Planned

Actively Developed

St3ix Obfuscator is under active development. New features are added regularly, and existing behavior is refined based on real-world use. Contributions, feedback, and feature requests are welcome via Discord or GitHub issues.

FAQ

Which Java version is required?

Java 17 or later. The obfuscator and your input JARs must run on a compatible JVM.

Is it similar to ProGuard?

St3ix focuses on obfuscation (renaming, flow, strings, numbers). ProGuard also shrinks and optimizes code. Use St3ix when you want strong obfuscation without tree-shaking.

Does it work with Bukkit / Minecraft plugins?

Yes. Use excludeClasses in config.yml to exclude Bukkit, Spigot, or other framework packages. See the documentation for patterns.

Can I use it from the command line or CI/CD?

Yes. Use java -jar st3ix-obfuscator.jar -i input.jar -o output.jar. Perfect for Gradle tasks, GitHub Actions, or other build pipelines.

Ready to protect your Java applications?

Read the Documentation