A powerful and flexible Java bytecode obfuscator built with ASM that provides comprehensive obfuscation capabilities while maintaining fine-grained control over what gets obfuscated.
- Class Renaming - Rename classes to obscure names while preserving functionality
- Method Renaming - Obfuscate method names with intelligent handling of constructors, synthetic methods, and inheritance
- Field Renaming - Rename fields while maintaining proper access relationships
- Reference Updating - Automatically updates all references to renamed elements throughout the codebase
- Inheritance-Aware Renaming - Properly handles interface implementations and method overrides
- Multiple Naming Modes - Choose from sequential, alphabetic, random short/long, or single character naming schemes
- Obfuscation Levels - Pre-defined security levels (MINIMAL, BASIC, AGGRESSIVE, EXTREME, CUSTOM)
- Keep Rules System - Sophisticated rules for preserving specific classes, methods, and fields
- Pattern Matching - Use regex patterns to define keep rules for multiple elements at once
- Package Filtering - Include/exclude specific packages with fine-grained control
- Granular Control - Specify exactly what to keep at the class, method, and field level
- Configuration Files - Enhanced JSON-based configuration with nested structure and presets
- Configuration Validation - Comprehensive validation with detailed warnings and error reporting
- Command Line Interface - Full-featured CLI with extensive options
- Transformer Pipeline - Modular transformer system with priority-based execution
- Abstract Base Classes - Easy to extend with custom obfuscation techniques
- Plugin System - Add new transformers without modifying core code
- Context Sharing - Transformers can share data and coordinate operations
- Rich Console Output - Beautiful logging with emojis and color coding
- Timestamped Logs - All operations are timestamped for debugging
- Progress Tracking - Visual progress indicators and statistics
- Mapping Generation - Generate detailed mappings of original to obfuscated names
- Obfuscation Resistance Scoring - Comprehensive analysis of obfuscation strength with recommendations
- Verbose Mode - Detailed logging of all transformations performed
- Validation System - Comprehensive validation with warnings and error detection
- Performance Monitoring - Execution statistics and optimization recommendations
- Method References - Properly handles Java 8+ method references (::) in lambda expressions
- Synthetic Methods - Intelligently handles compiler-generated synthetic methods
- Bridge Methods - Correct handling of bridge methods in inheritance hierarchies
- Entry Point Protection - Automatic preservation of main methods and constructors
- JAR Structure - Preserves non-class files (resources, manifests, etc.)
- Multi-threading - Configurable parallel processing for improved performance
- Backup System - Automatic backup creation with configurable directory
- String Compression - Advanced string obfuscation and compression techniques
- Code Optimization - Built-in bytecode optimization during obfuscation
Download the latest release or build from source:
git clone https://github.com/cvs0/java-bytecode-obfuscator.git
cd java-bytecode-obfuscator
./gradlew build
# Simple obfuscation with all features enabled
java -jar java-bytecode-obfuscator-1.0-SNAPSHOT.jar input.jar output.jar \
--rename-classes --rename-methods --rename-fields --rename-local-variables \
\
--obfuscate-conditions --compress-strings --compress-strings
# Maximum security with anti-debugging and resistance scoring
java -jar java-bytecode-obfuscator-1.0-SNAPSHOT.jar input.jar output.jar \
--rename-classes --rename-methods --rename-fields --rename-local-variables \
--obfuscate-conditions --compress-strings --compress-strings --anti-debugging \
\
--debugger-action EXIT_WITH_ERROR --generate-score --naming-mode RANDOM_LONG
# With main class protection
java -jar java-bytecode-obfuscator-1.0-SNAPSHOT.jar input.jar output.jar \
-m com.example.Main --keep-main-class --keep-entry-points \
--rename-classes --rename-methods --rename-fields --rename-local-variables \
\
--obfuscate-conditions --compress-strings
# Generate mappings and resistance score
java -jar java-bytecode-obfuscator-1.0-SNAPSHOT.jar input.jar output.jar \
--mappings mappings.txt --generate-score --verbose \
--rename-classes --rename-methods --rename-fields --rename-local-variables \
\
--obfuscate-conditions --compress-strings --compress-strings
# Use different naming modes
java -jar java-bytecode-obfuscator-1.0-SNAPSHOT.jar input.jar output.jar \
--naming-mode RANDOM_SHORT --rename-classes --rename-methods --rename-fields \
\
--obfuscate-conditions --compress-strings --compress-strings
java -jar java-bytecode-obfuscator-1.0-SNAPSHOT.jar input.jar output.jar \
--naming-mode SEQUENTIAL_ALPHA --verbose \
--rename-classes --rename-methods --rename-fields --rename-local-variables \
\
--obfuscate-conditions --compress-strings --compress-strings
Create a comprehensive JSON configuration file:
{
"obfuscationLevel": "AGGRESSIVE",
"mainClass": "com/example/MyApp",
"obfuscation": {
"renameClasses": true,
"renameFields": true,
"renameMethods": true,
"renameLocalVariables": true,
"obfuscateConditions": true,
"compressStrings": true,
"shuffleMembers": true,
"optimizeCode": true
},
"naming": {
"namingMode": "RANDOM_SHORT"
},
"security": {
"antiDebugging": true,
"debuggerAction": "CORRUPT_EXECUTION"
},
"packages": {
"includePackages": ["com/example/core", "com/example/business"],
"excludePackages": ["com/example/test", "com/example/debug"]
},
"keepRules": {
"keepMainClass": true,
"keepStandardEntryPoints": true,
"keepClasses": ["com/example/api/PublicAPI"],
"keepClassPatterns": [".*Controller", ".*Exception"],
"keepMethods": {
"com/example/api/PublicAPI": ["publicMethod"]
},
"keepMethodPatterns": {
".*Controller": ["handle.*", "process.*"]
}
},
"debugging": {
"preserveLineNumbers": false,
"preserveLocalVariableNames": false,
"verbose": false,
"generateScore": true
},
"performance": {
"maxThreads": 4,
"sequentialTransformers": false
},
"backup": {
"enableBackup": true,
"backupDir": "./backups"
},
"customSettings": {
"stringEncryptionKey": "myCustomKey123",
"customTransformerEnabled": true
}
}
import net.cvs0.Obfuscator;
import net.cvs0.config.ObfuscationConfig;
ObfuscationConfig config = new ObfuscationConfig.Builder()
.mainClass("com/example/Application")
.renameClasses(true)
.renameFields(true)
.renameMethods(true)
.renameLocalVariables(true)
.obfuscateConditions(true)
.compressStrings(true)
.compressStrings(true)
.namingMode(NamingMode.RANDOM_SHORT)
.verbose(true)
// Keep specific classes
.keepClass("com/example/api/PublicAPI")
.keepClassPattern("com/example/config/.*")
// Keep specific methods
.keepClassMethod("com/example/Service", "publicMethod")
.keepClassMethodPattern(".*Controller", "handle.*")
.keepAllMethodsForClass("com/example/PublicInterface")
// Keep specific fields
.keepClassField("com/example/Constants", "VERSION")
.keepClassFieldPattern(".*Entity", "id")
.keepAllFieldsForClass("com/example/Config")
// Convenience methods
.keepMainClass()
.keepStandardEntryPoints()
.build();
Obfuscator obfuscator = new Obfuscator();
obfuscator.obfuscate(inputJar, outputJar, config, mappingsFile);
The obfuscator provides pre-defined security levels that automatically configure optimal settings for different use cases:
Level | Description | Use Case | Settings |
---|---|---|---|
MINIMAL | Basic protection with minimal impact | Development, debugging | Classes only, sequential naming |
BASIC | Standard obfuscation for most applications | Production applications | Classes, fields, methods |
AGGRESSIVE | Heavy obfuscation with advanced techniques | Sensitive applications | All features, anti-debugging, shuffling |
EXTREME | Maximum security with performance trade-offs | High-security applications | All features, maximum settings |
CUSTOM | User-defined configuration | Specific requirements | Manual configuration |
# Use predefined levels
java -jar obfuscator.jar input.jar output.jar --level AGGRESSIVE
# Override specific settings
java -jar obfuscator.jar input.jar output.jar --level BASIC --anti-debugging
// Quick level selection
ObfuscationConfig config = ConfigPresets.createPresetForLevel(ObfuscationLevel.AGGRESSIVE)
.mainClass("com/example/Main")
.keepMainClass()
.build();
// Pre-built presets
ObfuscationConfig prodConfig = ConfigPresets.createProductionObfuscation()
.mainClass("com/example/Main")
.build();
ObfuscationConfig devConfig = ConfigPresets.createDevelopmentObfuscation()
.mainClass("com/example/Main")
.build();
ObfuscationConfig secureConfig = ConfigPresets.createSecureObfuscation()
.mainClass("com/example/Main")
.build();
- Class renaming only
- Sequential naming with prefix
- No anti-debugging
- Preserves debugging information
- Fast processing
- Class, field, and method renaming
- Sequential alphabetic naming
- Basic keep rules
- Balanced security and performance
- All renaming features
- Local variable obfuscation
- Condition obfuscation
- Anti-debugging protection
- Member shuffling
- Random short naming
- Maximum obfuscation settings
- String compression
- Code optimization
- Advanced anti-debugging
- Random long naming
- All security features
The obfuscator supports multiple naming modes to generate obfuscated names:
Mode | Description | Example Output |
---|---|---|
SEQUENTIAL_PREFIX |
Sequential with prefix (default) | a1 , a2 , a3 , m1 , m2 , f1 , f2 |
SEQUENTIAL_ALPHA |
Sequential alphabetic | a , b , c , aa , ab , ac |
RANDOM_SHORT |
Random short names (4 characters) | abcd , xyzk , mnop , qrst |
RANDOM_LONG |
Random long names (8-16 characters) | abcdefgh , xyzklmnopqrs |
SINGLE_CHAR |
Single character names | a , b , c , then falls back to a1 , a2 |
# Use random short names for maximum obfuscation
java -jar obfuscator.jar input.jar output.jar --naming-mode RANDOM_SHORT
# Use single character names for minimal size
java -jar obfuscator.jar input.jar output.jar --naming-mode SINGLE_CHAR
# Use alphabetic sequence for readability in testing
java -jar obfuscator.jar input.jar output.jar --naming-mode SEQUENTIAL_ALPHA
{
"namingMode": "RANDOM_SHORT",
"renameClasses": true,
"renameFields": true,
"renameMethods": true
}
import net.cvs0.config.NamingMode;
ObfuscationConfig config = new ObfuscationConfig.Builder()
.namingMode(NamingMode.RANDOM_LONG)
.renameClasses(true)
.renameFields(true)
.renameMethods(true)
.build();
Condition obfuscation transforms simple boolean constants (true
and false
) into mathematically equivalent complex expressions that make the code harder to understand and analyze.
The transformer replaces simple boolean constants with arithmetic expressions that evaluate to the same value:
Original Code:
if (someFlag == true) {
doSomething();
}
boolean result = false;
Obfuscated Code:
// true becomes: 2 - 1
if (someFlag == (2 - 1)) {
doSomething();
}
// false becomes: 1 - 1
boolean result = (1 - 1);
- Safe Transformation - Only transforms constants that are likely to be boolean conditions (10% probability to avoid breaking non-boolean integer usage)
- Multiple Strategies - Uses various mathematical expressions to avoid patterns
- Stackmap-Safe - Generates bytecode that passes JVM verification
- Conservative Approach - Only targets simple constant loading to maintain program correctness
# Enable condition obfuscation via CLI
java -jar obfuscator.jar input.jar output.jar --obfuscate-conditions
# Combined with other obfuscation techniques
java -jar obfuscator.jar input.jar output.jar \
--rename-classes --rename-methods --rename-fields \
--obfuscate-conditions --compress-strings --compress-strings --verbose
{
"renameClasses": true,
"renameFields": true,
"renameMethods": true,
"obfuscateConditions": true,
"compressStrings": true,
"compressStrings": true,
"verbose": true
}
ObfuscationConfig config = new ObfuscationConfig.Builder()
.renameClasses(true)
.renameFields(true)
.renameMethods(true)
.obfuscateConditions(true)
.compressStrings(true)
.compressStrings(true)
.build();
Before Obfuscation:
public boolean isEnabled() {
return true;
}
public void process() {
if (false) {
handleError();
}
}
After Obfuscation:
public boolean a() {
return 3 - 2; // Evaluates to 1 (true)
}
public void b() {
if (5 - 5) { // Evaluates to 0 (false)
c();
}
}
## ποΈ String Compression
String compression transforms string literals into compressed, base64-encoded data that is decompressed at runtime. This reduces JAR size and makes string analysis more difficult.
### How It Works
The transformer identifies string literals and compresses them using the Deflate algorithm, then encodes them with Base64. At runtime, the compressed strings are decompressed transparently.
**Original Code:**
```java
public class Example {
public static void main(String[] args) {
String message = "This is a very long string that contains a lot of repetitive text and should be compressed effectively using the deflate algorithm for better obfuscation."; (transforms boolean constants)
System.out.println("Application started with message: " + message);
}
}
Obfuscated Code:
public class a1 {
public static void main(String[] args) {
String a = new String(new java.util.zip.Inflater().inflate(java.util.Base64.getDecoder().decode("eJwLycxNzStJzSvJTFHwSM3JzM+Lz8nPS1WwMjQwMDK1UjA0NDEwMLRSULA0NDQwsDK0MjSyMjKzMjKzMjKzMjK...")), 0, 147, java.nio.charset.StandardCharsets.UTF_8);
System.out.println("Application started with message: " + a);
}
}
- Intelligent String Selection - Only compresses strings that benefit from compression (length > 10 characters)
- Special String Detection - Avoids compressing URLs, configuration strings, class names, and other special patterns
- Compression Efficiency - Only applies compression if the result is actually smaller
- Configurable Probability - Randomly compresses strings to avoid predictable patterns (70% chance by default)
- Runtime Transparent - Decompression happens automatically at runtime
- Keep Rules Aware - Respects keep rules and doesn't compress strings in preserved classes
# Enable string compression via CLI
java -jar obfuscator.jar input.jar output.jar --compress-strings
# Combined with other obfuscation techniques
java -jar obfuscator.jar input.jar output.jar \
--rename-classes --rename-methods --rename-fields \
--compress-strings --obfuscate-conditions --verbose
{
"obfuscation": {
"renameClasses": true,
"renameFields": true,
"renameMethods": true,
"compressStrings": true
},
"debugging": {
"verbose": true
}
}
ObfuscationConfig config = new ObfuscationConfig.Builder()
.renameClasses(true)
.renameFields(true)
.renameMethods(true)
.compressStrings(true)
.verbose(true)
.build();
Obfuscator obfuscator = new Obfuscator();
obfuscator.obfuscate(inputJar, outputJar, config, mappingsFile);
The transformer automatically determines which strings to compress based on:
Criteria | Description | Example |
---|---|---|
Minimum Length | Strings must be at least 10 characters | "Short" β Not compressed |
Special Patterns | Avoids URLs, config strings, file extensions | "https://example.com" β Not compressed |
Compression Efficiency | Only compresses if result is smaller | "aaaaaaaaaa" β Compressed |
Class Keep Rules | Respects keep rules for classes | Kept classes β Strings not compressed |
Random Selection | 70% probability to avoid patterns | Random selection to prevent analysis |
- Size Reduction - Compresses repetitive or long strings effectively
- Content Obfuscation - Makes string analysis much more difficult
- Runtime Performance - Minimal impact on application startup
- Reverse Engineering Protection - Strings are not visible in decompiled code
- Pattern Breaking - Random compression prevents pattern-based analysis
- Compression Time - Adds minimal time during obfuscation
- Runtime Overhead - Small one-time decompression cost per string
- Memory Usage - Slightly higher memory usage during decompression
- JAR Size - Usually reduces overall JAR size for applications with many long strings
- Combine with Other Features - Use alongside renaming and condition obfuscation
- Test Performance - Profile your application after string compression
- Verify Functionality - Ensure compressed strings work correctly in your use case
- Monitor JAR Size - Check if compression actually reduces your JAR size
Long Strings (Compressed):
- Error messages and user notifications
- SQL queries and database scripts
- Configuration templates
- Help text and documentation strings
Short/Special Strings (Not Compressed):
- Class names and package names
- URLs and file paths
- Single words and short constants
- Format strings and patterns
Method inlining replaces method calls with the actual method body, reducing call overhead and making the code harder to analyze by eliminating method boundaries.
The transformer identifies simple methods that are good candidates for inlining and replaces calls to these methods with their actual implementation.
Original Code:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int calculate() {
return add(5, 3); // Method call
}
}
Obfuscated Code:
public class a1 {
public int a1(int a, int b) {
return a + b; // Method still exists but may not be called
}
public int a2() {
return 5 + 3; // Method call inlined
}
}
- Smart Method Selection - Only inlines simple methods without complex control flow
- Safety Checks - Avoids inlining methods with field access or complex operations
- Conservative Approach - Prevents inlining that could break functionality
- Stack-Safe - Generates proper bytecode that passes JVM verification
- Selective Inlining - Only processes methods that benefit from inlining
# Enable method inlining via CLI
java -jar obfuscator.jar input.jar output.jar --inline-simple-methods
# Combined with other obfuscation techniques
java -jar obfuscator.jar input.jar output.jar \
--rename-classes --rename-methods --inline-simple-methods \
--obfuscate-conditions --verbose
{
"obfuscation": {
"renameClasses": true,
"renameMethods": true,
"inlineSimpleMethods": true
}
}
Methods are considered for inlining if they:
- Are short (< 5 instructions)
- Don't access fields
- Don't contain loops or branches
- Don't throw exceptions
- Are not constructors or static initializers
Fake interface flooding generates synthetic interfaces and makes classes implement them, creating false inheritance relationships that confuse reverse engineering tools and static analysis.
The transformer creates fake interfaces with random methods and makes target classes implement these interfaces, adding noise to the class hierarchy without affecting functionality.
Original Code:
public class DataProcessor {
public void process(String data) {
System.out.println("Processing: " + data);
}
}
Obfuscated Code:
// Generated fake interfaces
interface a1 {
void m1();
int m2(String s);
}
interface a2 {
boolean m3();
}
public class a3 implements a1, a2 {
public void process(String data) { // Original method
System.out.println("Processing: " + data);
}
// Fake implementations that never execute
public void m1() { throw new UnsupportedOperationException(); }
public int m2(String s) { throw new UnsupportedOperationException(); }
public boolean m3() { throw new UnsupportedOperationException(); }
}
- Dynamic Interface Generation - Creates unique interfaces for each obfuscation run
- Configurable Quantity - Control how many fake interfaces are added per class
- Method Variety - Generates diverse method signatures with different return types
- Exception Throwing - Fake methods throw exceptions to prevent accidental execution
- Inheritance Safe - Doesn't interfere with existing inheritance hierarchies
# Enable fake interface flooding with default count (10)
java -jar obfuscator.jar input.jar output.jar --flood-fake-interfaces
# Specify custom interface count
java -jar obfuscator.jar input.jar output.jar --flood-fake-interfaces --fake-interface-count 25
# Combined with other obfuscation
java -jar obfuscator.jar input.jar output.jar \
--rename-classes --flood-fake-interfaces --fake-interface-count 15 \
--rename-methods --verbose
{
"obfuscation": {
"renameClasses": true,
"renameMethods": true,
"floodFakeInterfaces": true,
"fakeInterfaceCount": 20
}
}
- Analysis Confusion - Makes automated tools report false inheritance relationships
- Code Bloat - Increases apparent code complexity
- Pattern Breaking - Disrupts class hierarchy analysis
- Reverse Engineering Difficulty - Makes understanding class relationships harder
Fake exception insertion adds conditional exception checks throughout the code that appear functional but never actually execute, creating complex control flow that complicates static analysis.
The transformer inserts exception checks with conditions that are mathematically impossible to satisfy, adding apparent complexity without affecting program execution.
Original Code:
public void processData(String input) {
if (input != null) {
System.out.println("Processing: " + input);
}
}
Obfuscated Code:
public void a1(String a2) {
// Fake exception check - hash will never equal Integer.MAX_VALUE
"fake_check_123".hashCode(); // Complex calculation that's discarded
if (a2 != null) {
// Another fake check - 1.0 + 2.0 never equals 3.0 comparison
1.0 + 2.0; // Result discarded
System.out.println("Processing: " + a2);
}
}
- Non-Executing Checks - All fake checks are mathematically designed to never trigger
- Diverse Patterns - Uses various types of calculations (string hash, math operations, etc.)
- Stack-Safe Operations - All operations properly manage the JVM stack
- Conservative Insertion - Only adds checks at safe insertion points
- Performance Neutral - Minimal runtime impact from simple calculations
# Enable fake exception insertion
java -jar obfuscator.jar input.jar output.jar --insert-fake-exceptions
# Combined with comprehensive obfuscation
java -jar obfuscator.jar input.jar output.jar \
--rename-classes --rename-methods --insert-fake-exceptions \
--obfuscate-conditions --compress-strings --verbose
{
"obfuscation": {
"renameClasses": true,
"renameMethods": true,
"insertFakeExceptions": true,
"obfuscateConditions": true
}
}
The transformer uses several types of fake checks:
Type | Description | Example |
---|---|---|
String Hash | Computes hash codes that never match target values | "fake_check".hashCode() |
Math Operations | Simple arithmetic with known results | 1.0 + 2.0 |
String Length | String length calculations | "test".length() |
- Static Analysis Confusion - Makes control flow appear more complex
- Code Pattern Disruption - Breaks up recognizable code patterns
- Reverse Engineering Difficulty - Adds apparent exception handling logic
- Minimal Performance Impact - Simple operations with negligible runtime cost
- Combine with Other Techniques - Use alongside renaming and condition obfuscation
- Test Thoroughly - Verify that fake checks don't interfere with application logic
- Monitor Performance - While minimal, ensure no unexpected performance impact
- Validate Functionality - Confirm all original exception handling still works correctly
Usage: obfuscator [OPTIONS] <input-jar> <output-jar>
Arguments:
<input-jar> Input JAR file to obfuscate
<output-jar> Output JAR file
Core Options:
-c, --config <file> Configuration file (JSON)
-m, --main-class <class> Main class name
-l, --level <level> Obfuscation level (MINIMAL, BASIC, AGGRESSIVE, EXTREME, CUSTOM)
--mappings <file> Output mappings file
-v, --verbose Enable verbose output
-h, --help Show help
--version (deflate/base64 encoding) Show version
Obfuscation Options:
--rename-classes Enable class renaming
--rename-fields Enable field renaming
--rename-methods Enable method renaming
--rename-local-variables Enable local variable renaming
--obfuscate-conditions Enable condition obfuscation (transforms boolean constants)
--compress-strings Enable string compression (deflate/base64 encoding)
--inline-simple-methods Enable simple method inlining
--flood-fake-interfaces Enable fake interface flooding
--fake-interface-count <n> Number of fake interfaces per class (1-50, default: 10)
--insert-fake-exceptions Insert fake exception checks
--shuffle-members Enable member shuffling
--optimize-code Enable code optimization
Naming Options:
-n, --naming-mode <mode> Name generation mode (SEQUENTIAL_PREFIX, SEQUENTIAL_ALPHA,
RANDOM_SHORT, RANDOM_LONG, SINGLE_CHAR)
Security Options:
--anti-debugging Enable anti-debugging protection
--debugger-action <action> Debugger response (EXIT_SILENTLY, EXIT_WITH_ERROR, CORRUPT_EXECUTION)
Package Options:
--include-package <pkg> Include specific package (repeatable)
--exclude-package <pkg> Exclude specific package (repeatable)
Keep Rules:
--keep-class <class> Keep specific class (repeatable)
--keep-class-pattern <pattern> Keep classes matching pattern (repeatable)
--keep-main-class Keep the main class
--keep-entry-points Keep standard entry points
Debugging Options:
--preserve-line-numbers Preserve line numbers for debugging
--preserve-local-vars Preserve local variable names
--generate-score Generate obfuscation resistance score
Performance Options:
--max-threads <n> Maximum number of threads
--sequential Use sequential transformer processing
--enable-backup Enable backup creation
--backup-dir <dir> Backup directory path
# Basic obfuscation
java -jar obfuscator.jar input.jar output.jar
# With configuration file
java -jar obfuscator.jar -c config.json input.jar output.jar
# Command line configuration
java -jar obfuscator.jar input.jar output.jar \
--main-class com/example/Main \
--keep-main-class \
--keep-entry-points \
--obfuscate-conditions \
--verbose
# Keep specific classes
java -jar obfuscator.jar input.jar output.jar \
--keep-class "com/example/api/PublicAPI" \
--keep-class-pattern ".*Controller" \
--keep-class-pattern ".*Service"
# Generate mappings
java -jar obfuscator.jar input.jar output.jar --mappings mappings.txt
# Use different naming modes
java -jar obfuscator.jar input.jar output.jar \
--naming-mode RANDOM_LONG \
--rename-classes --rename-methods --rename-fields --obfuscate-conditions
java -jar obfuscator.jar input.jar output.jar \
--naming-mode SINGLE_CHAR \
--obfuscate-conditions \
--verbose
# Override config file settings
java -jar obfuscator.jar -c config.json input.jar output.jar \
--rename-classes false \
--naming-mode SEQUENTIAL_ALPHA \
--obfuscate-conditions \
--verbose
Control which packages are processed during obfuscation with fine-grained include/exclude rules:
Only process classes in specified packages:
java -jar obfuscator.jar input.jar output.jar \
--include-package com.example.core \
--include-package com.example.business \
--rename-classes --rename-methods --rename-fields
Skip processing of specified packages:
java -jar obfuscator.jar input.jar output.jar \
--exclude-package com.example.test \
--exclude-package com.example.debug \
--rename-classes --rename-methods --rename-fields
{
"packages": {
"includePackages": [
"com/example/core",
"com/example/business",
"com/example/services"
],
"excludePackages": [
"com/example/test",
"com/example/debug",
"com/example/tools"
]
}
}
ObfuscationConfig config = new ObfuscationConfig.Builder()
.includePackage("com/example/core")
.includePackage("com/example/business")
.excludePackage("com/example/test")
.excludePackage("com/example/debug")
.renameClasses(true)
.renameFields(true)
.renameMethods(true)
.build();
- Exclude takes precedence - If a package matches both include and exclude, it will be excluded
- Hierarchical matching -
com/example
will matchcom/example/core/Service
- No rules = process all - If no include/exclude rules are specified, all packages are processed
- Validation - Invalid package names will generate configuration errors
{
"obfuscationLevel": "AGGRESSIVE",
"mainClass": "com/example/MyApp",
"obfuscation": {
"renameClasses": true,
"renameFields": true,
"renameMethods": true,
"renameLocalVariables": true,
"obfuscateConditions": true,
"compressStrings": true,
"shuffleMembers": true,
"optimizeCode": true
},
"naming": {
"namingMode": "RANDOM_SHORT"
},
"security": {
"antiDebugging": true,
"debuggerAction": "CORRUPT_EXECUTION"
},
"packages": {
"includePackages": ["com/example/core", "com/example/business"],
"excludePackages": ["com/example/test", "com/example/debug"]
},
"keepRules": {
"keepMainClass": true,
"keepStandardEntryPoints": true,
"keepClasses": ["com/example/api/PublicAPI"],
"keepClassPatterns": [".*Controller", ".*Exception"]
},
"debugging": {
"preserveLineNumbers": false,
"verbose": false,
"generateScore": true
},
"performance": {
"maxThreads": 4,
"sequentialTransformers": false
},
"backup": {
"enableBackup": true,
"backupDir": "./backups"
}
}
{
"obfuscationLevel": "BASIC",
"mainClass": "com/example/Main",
"keepRules": {
"keepMainClass": true,
"keepStandardEntryPoints": true
},
"debugging": {
"verbose": true
}
}
{
"mainClass": "com/example/Application",
"renameClasses": true,
"renameFields": true,
"renameMethods": true,
"renameLocalVariables": true,
"obfuscateConditions": true,
"namingMode": "RANDOM_SHORT",
"verbose": false,
"keepRules": {
"keepMainClass": true,
"keepStandardEntryPoints": true,
"keepClassPatterns": [
".*Application", ".*Controller", ".*Configuration", ".*Config.*"
],
"keepMethodPatterns": {
".*Controller": [".*"],
".*Service": [".*public.*"],
".*Repository": [".*"]
},
"keepFieldPatterns": {
".*Entity": [".*"],
".*Configuration": [".*"],
".*Properties": [".*"]
}
}
}
{
"renameClasses": false,
"renameFields": true,
"renameMethods": false,
"namingMode": "SINGLE_CHAR",
"verbose": false,
"keepRules": {
"keepStandardEntryPoints": true,
"keepClassPatterns": ["com/mylib/api/.*", "com/mylib/public/.*"],
"keepMethodPatterns": {
".*": [".*public.*"]
},
"keepAllMethods": ["com/mylib/PublicInterface"],
"keepFieldPatterns": {
"com/mylib/api/.*": [".*"],
"com/mylib/constants/.*": [".*"]
}
}
}
{
"mainClass": "com/example/MyApp",
"renameClasses": true,
"renameFields": true,
"renameMethods": true,
"renameLocalVariables": true,
"obfuscateConditions": true,
"namingMode": "RANDOM_LONG",
"verbose": true,
"keepRules": {
"keepMainClass": true,
"keepStandardEntryPoints": true,
"keepClasses": ["com/example/api/PublicAPI"],
"keepClassPatterns": [".*Controller", "com/example/dto/.*"],
"keepMethods": {
"com/example/Service": [
"publicMethod",
{"name": "specificMethod", "descriptor": "(Ljava/lang/String;)V"}
]
},
"keepMethodPatterns": {
".*Controller": ["handle.*", "process.*"],
".*Service": ["get.*", "set.*"]
},
"keepAllMethods": ["com/example/PublicInterface"],
"keepFields": {
"com/example/Constants": ["VERSION", "BUILD_DATE"]
},
"keepFieldPatterns": {
".*Entity": ["id", ".*Date"],
".*Model": [".*"]
},
"keepAllFields": ["com/example/GlobalConstants"]
}
}
Method | Description | Example |
---|---|---|
keepClass(String) |
Keep specific class | .keepClass("com/example/API") |
keepClassPattern(String) |
Keep classes matching pattern | .keepClassPattern(".*Controller") |
Method | Description | Example |
---|---|---|
keepClassMethod(String, String) |
Keep specific method | .keepClassMethod("MyClass", "publicMethod") |
keepClassMethodWithDescriptor(String, String, String) |
Keep method with descriptor | .keepClassMethodWithDescriptor("MyClass", "method", "(I)V") |
keepClassMethodPattern(String, String) |
Keep methods matching pattern | .keepClassMethodPattern(".*Service", "get.*") |
keepAllMethodsForClass(String) |
Keep all methods in class | .keepAllMethodsForClass("PublicAPI") |
Method | Description | Example |
---|---|---|
keepClassField(String, String) |
Keep specific field | .keepClassField("Config", "VERSION") |
keepClassFieldPattern(String, String) |
Keep fields matching pattern | .keepClassFieldPattern(".*Entity", "id") |
keepAllFieldsForClass(String) |
Keep all fields in class | .keepAllFieldsForClass("Constants") |
Method | Description |
---|---|
keepMainClass() |
Keep the main class (requires mainClass() to be set) |
keepStandardEntryPoints() |
Keep main methods, constructors, and static initializers |
public class CustomTransformer extends AbstractTransformer {
public CustomTransformer() {
super("CustomTransformer", 400); // Name and priority
}
@Override
public void transform(ClassReader reader, ClassWriter writer, ObfuscationContext context) {
// Your custom transformation logic
logTransformation("Applying custom transformation", context);
// Apply transformation
ClassVisitor visitor = new CustomVisitor(writer, context);
reader.accept(visitor, 0);
}
@Override
public boolean isEnabled(ObfuscationContext context) {
return context.getConfig().getProperty("enableCustom", Boolean.class, false);
}
}
// Register custom transformer
Obfuscator obfuscator = new Obfuscator();
obfuscator.getEngine().registerTransformer(new CustomTransformer());
The enhanced validation system provides comprehensive checking with detailed feedback:
// Validate complete configuration
ObfuscationConfig config = // ... your config
ConfigValidator.ValidationResult result = ConfigValidator.validate(config);
if (result.hasErrors()) {
System.err.println("Configuration errors:");
result.getErrors().forEach(error -> System.err.println(" β " + error));
return;
}
if (result.hasWarnings()) {
System.out.println("Configuration warnings:");
result.getWarnings().forEach(warning -> System.out.println(" β οΈ " + warning));
}
// Validate builder before building
ObfuscationConfig.Builder builder = new ObfuscationConfig.Builder()
.renameClasses(true)
.maxThreads(0); // Invalid setting
if (!builder.isValid()) {
ConfigValidator.ValidationResult validation = builder.validate();
validation.getErrors().forEach(System.err::println);
}
ObfuscationConfig config = builder.build(); // Will throw if invalid
- Package name validation - Ensures valid Java package names
- Class name validation - Checks for valid class names and patterns
- Performance settings - Validates thread counts and backup directories
- Consistency checks - Detects conflicting settings
- Security warnings - Identifies potential security issues
- Best practice recommendations - Suggests optimal configurations
Type | Message | Solution |
---|---|---|
Error | "Max threads must be at least 1" | Set maxThreads to 1 or higher |
Error | "Backup enabled but no directory specified" | Set backupDir when enableBackup is true |
Warning | "Anti-debugging enabled but line numbers preserved" | Disable preserveLineNumbers for better security |
Warning | "Include package is within excluded package" | Review package include/exclude rules |
The obfuscator supports multi-threaded processing and performance optimization:
// Configure thread count
ObfuscationConfig config = new ObfuscationConfig.Builder()
.maxThreads(8) // Use 8 threads
.sequentialTransformers(false) // Enable parallel processing
.build();
// Auto-detect optimal thread count
ObfuscationConfig config = new ObfuscationConfig.Builder()
.maxThreads(Runtime.getRuntime().availableProcessors())
.build();
- Each transformer processes all classes simultaneously
- Better performance for large projects
- Uses multiple CPU cores efficiently
- Each transformer processes one class at a time
- More predictable memory usage
- Better for memory-constrained environments
// Enable sequential processing
ObfuscationConfig config = new ObfuscationConfig.Builder()
.sequentialTransformers(true)
.maxThreads(1) // Limited threading benefit with sequential
.build();
ObfuscationConfig config = new ObfuscationConfig.Builder()
.optimizeCode(true) // Enable bytecode optimization
.maxThreads(8) // Utilize multiple cores
.sequentialTransformers(false) // Parallel processing
.enableBackup(false) // Skip backup for speed (not recommended)
.verbose(false) // Reduce logging overhead
.build();
Automatic backup creation with configurable options:
ObfuscationConfig config = new ObfuscationConfig.Builder()
.enableBackup(true)
.backupDir("./backups") // Custom backup directory
.build();
# Performance-optimized processing
java -jar obfuscator.jar input.jar output.jar \
--max-threads 8 \
--optimize-code \
--enable-backup \
--backup-dir ./backups
# Memory-efficient processing
java -jar obfuscator.jar input.jar output.jar \
--max-threads 2 \
--sequential \
--no-backup
- Use appropriate thread count - Usually CPU cores Γ 1-2
- Enable code optimization - Improves output quality
- Disable backup for speed - Only in development/testing
- Use sequential for large JARs - Better memory management
- Monitor memory usage - Adjust thread count if needed
The generated mapping file contains the transformation mappings:
# Class mappings
com/example/MyClass -> a
com/example/Service -> b
# Field mappings
com/example/MyClass.fieldName -> a.a
com/example/Service.config -> b.b
# Method mappings
com/example/MyClass.methodName()V -> a.a()V
com/example/Service.processData(Ljava/lang/String;)I -> b.a(Ljava/lang/String;)I
// Quick preset selection
ObfuscationConfig minimal = ConfigPresets.createPresetForLevel(ObfuscationLevel.MINIMAL)
.mainClass("com/example/Main")
.keepMainClass()
.build();
// Production-ready configuration
ObfuscationConfig production = ConfigPresets.createProductionObfuscation()
.mainClass("com/example/Main")
.keepMainClass()
.build();
// Development configuration with debugging
ObfuscationConfig development = ConfigPresets.createDevelopmentObfuscation()
.mainClass("com/example/Main")
.keepMainClass()
.build();
// Maximum security configuration
ObfuscationConfig secure = ConfigPresets.createSecureObfuscation()
.mainClass("com/example/Main")
.keepMainClass()
.build();
ObfuscationConfig config = new ObfuscationConfig.Builder()
.obfuscationLevel(ObfuscationLevel.MINIMAL)
.renameClasses(false)
.renameFields(true) // Only obfuscate fields
.renameMethods(false)
.namingMode(NamingMode.SINGLE_CHAR)
.keepStandardEntryPoints()
.build();
ObfuscationConfig config = new ObfuscationConfig.Builder()
.obfuscationLevel(ObfuscationLevel.AGGRESSIVE)
.renameClasses(true)
.renameFields(true)
.renameMethods(true)
.renameLocalVariables(true)
.obfuscateConditions(true)
.antiDebugging(true)
.shuffleMembers(true)
.namingMode(NamingMode.RANDOM_LONG)
.keepStandardEntryPoints()
.build();
ObfuscationConfig config = ConfigPresets.createPerformanceOptimizedObfuscation()
.mainClass("com/example/Main")
.maxThreads(Runtime.getRuntime().availableProcessors())
.optimizeCode(true)
.keepMainClass()
.build();
// Sequential with prefix (default)
ObfuscationConfig config1 = new ObfuscationConfig.Builder()
.namingMode(NamingMode.SEQUENTIAL_PREFIX)
.renameClasses(true)
.build();
// Random short names for good obfuscation
ObfuscationConfig config2 = new ObfuscationConfig.Builder()
.namingMode(NamingMode.RANDOM_SHORT)
.renameClasses(true)
.build();
// Single character for minimal size
ObfuscationConfig config3 = new ObfuscationConfig.Builder()
.namingMode(NamingMode.SINGLE_CHAR)
.renameClasses(true)
.build();
ObfuscationConfig config = new ObfuscationConfig.Builder()
.renameClasses(true)
.renameFields(true)
.renameMethods(true)
.namingMode(NamingMode.RANDOM_SHORT)
.keepClassPattern(".*Configuration")
.keepClassPattern(".*Controller")
.keepClassMethodPattern(".*Component", ".*")
.keepClassFieldPattern(".*Entity", ".*")
.keepStandardEntryPoints()
.build();
ObfuscationConfig config = new ObfuscationConfig.Builder()
.renameClasses(true)
.renameFields(true)
.renameMethods(true)
.namingMode(NamingMode.SEQUENTIAL_ALPHA)
.keepClassPattern(".*Activity")
.keepClassPattern(".*Service")
.keepClassPattern(".*BroadcastReceiver")
.keepClassMethodPattern(".*Activity", "onCreate.*")
.keepClassMethodPattern(".*Activity", "onResume.*")
.keepStandardEntryPoints()
.build();
- ObfuscationEngine - Main processing engine that orchestrates the obfuscation
- TransformerPipeline - Manages and executes transformers in priority order
- ObfuscationContext - Shared context containing configuration and mappings
- AbstractTransformer - Base class for all transformers with common functionality
-
ClassRenameTransformer (Priority: 100)
- Renames classes and updates all references
- Handles inheritance and interface implementations
-
FieldRenameTransformer (Priority: 200)
- Renames fields and updates field access instructions
- Preserves field access relationships
-
MethodRenameTransformer (Priority: 300)
- Renames methods and updates method invocations
- Skips constructors and static initializers
-
AntiDebuggingTransformer (Priority: 100)
- Adds anti-debugging checks and responses
- Configurable actions on debugger detection
- Adds VM checks to prevent debugging
-
ConditionObfuscationTransformer (Priority: 250)
- Transforms boolean constants into complex expressions
- Ensures stackmap safety
-
StringCompressionTransformer (Priority: 350)
- Compresses string literals using Deflate and Base64
- Decompresses strings at runtime
- Input Validation - Validates JAR files, output paths, and file permissions
- Configuration Validation - Validates keep rules, patterns, and configuration consistency
- Pattern Validation - Validates regex patterns and class name formats
.keepStandardEntryPoints() // Keep main methods, constructors, etc.
.keepClassPattern(".*Controller") // Instead of individual controllers
.keepClassMethodPattern(".*Entity", "get.*") // Keep all getters in entities
.verbose(true) // Enable detailed logging during development
ConfigValidator.ValidationResult result = ConfigValidator.validate(config);
// Check result before obfuscating
.keepClass("com/example/ReflectionUsedClass")
.keepAllMethodsForClass("com/example/ReflectionUsedClass")
ClassNotFoundException after obfuscation
- Ensure main class is kept:
.keepMainClass()
- Check if reflection is used: keep reflected classes
- Verify entry points are preserved:
.keepStandardEntryPoints()
NoSuchMethodError after obfuscation
- Keep public API methods:
.keepClassMethodPattern(".*API", ".*")
- Preserve interface implementations
- Check for dynamic method calls
Compilation errors with obfuscated JAR
- Validate keep rules with
ConfigValidator
- Use verbose logging to see what's being renamed
- Test with minimal obfuscation first
ObfuscationConfig config = ConfigPresets.createDebugObfuscation()
.verbose(true)
.build();
This project is licensed under the MIT License - see the LICENSE file for details.
- Added pre-defined security levels (MINIMAL, BASIC, AGGRESSIVE, EXTREME, CUSTOM)
- Created preset configurations for common use cases
- Quick level selection via CLI and programmatic API
- Include/exclude package filtering with hierarchical matching
- Fine-grained control over which packages to process
- Validation and conflict detection for package rules
- Multi-threaded processing with configurable thread counts
- Sequential vs parallel transformer processing modes
- Performance optimization settings and recommendations
- Automatic backup system with configurable directories
- Nested JSON configuration structure for better organization
- Custom settings support for extensibility
- String compression with deflate/base64 encoding
- Member shuffling and code optimization options
- Advanced debugging preservation options
- Comprehensive configuration validation with detailed feedback
- Real-time validation during configuration building
- Best practice recommendations and security warnings
- Package name and class name validation
- String compression using deflate/base64 encoding
- Anti-debugging protection with configurable responses
- Line number and local variable preservation options
- Obfuscation resistance scoring improvements
- Enhanced CLI with categorized options
- Builder pattern validation before configuration creation
- Comprehensive README with all new features
- Enhanced configuration examples
- Performance tuning guidelines
- Troubleshooting section improvements
- Added multiple naming modes (SEQUENTIAL_PREFIX, SEQUENTIAL_ALPHA, RANDOM_SHORT, RANDOM_LONG, SINGLE_CHAR)
- Enhanced CLI with naming mode selection
- Updated configuration file format to support naming modes
- Improved local variable renaming with configurable naming modes
- Initial release with class, method, and field renaming
- Advanced keep rules system
- Configuration presets
- Comprehensive validation
- Mapping generation
- Extensible transformer architecture