Skip to content

Commit 0fcd60f

Browse files
committed
Initial Commit
0 parents  commit 0fcd60f

23 files changed

+1292
-0
lines changed

.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

.gitignore

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.idea/*
2+
*.iml
3+
META-INF/*
4+
target/*
5+
out/*
6+
7+
# Compiled class file
8+
*.class
9+
10+
# Log file
11+
*.log
12+
13+
# BlueJ files
14+
*.ctxt
15+
16+
# Mobile Tools for Java (J2ME)
17+
.mtj.tmp/
18+
19+
# Package Files #
20+
*.jar
21+
*.war
22+
*.nar
23+
*.ear
24+
*.zip
25+
*.tar.gz
26+
*.rar
27+
28+
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
29+
hs_err_pid*

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) [year] [fullname]
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Bozar
2+
A Java bytecode obfuscator with GUI
3+
4+
![alt text](https://image.prntscr.com/image/GGDP-02JT-egDPFl_3L2xg.png)
5+
6+
## Usage
7+
* Download the version you want in [releases](https://github.com/vimasig/Bozar/releases) for your platform
8+
* Extract contents from the ZIP. <sub><sup>(That's not required by most ZIP tools)</sup></sub>
9+
* Run the executable.
10+
* Done.
11+
12+
Let me know if obfuscator fails. Submit an [issue](https://github.com/vimasig/Bozar/issues) here.
13+
14+
## Currently Available Obfuscation Options
15+
* Constant obfuscation <sub><sup>(String literals and numbers)</sup></sub>
16+
* Line number obfuscation
17+
* Local variable obfuscation
18+
* Source file/debug remover
19+
20+
## Command Line Arguments
21+
| Command | Description |
22+
| --- | --- |
23+
| `-input` | Target file path to obfuscate. |
24+
| `-output` | Output path. |
25+
| `-config` | Configuration path. |
26+
| `-console` | Application will run without GUI and obfuscation task will be started immediately. |

pom.xml

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<groupId>com.vimasig</groupId>
5+
<artifactId>Bozar</artifactId>
6+
<version>1.0.0</version>
7+
<properties>
8+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
9+
<maven.compiler.source>14</maven.compiler.source>
10+
<maven.compiler.target>14</maven.compiler.target>
11+
</properties>
12+
13+
<build>
14+
<plugins>
15+
<plugin>
16+
<groupId>org.apache.maven.plugins</groupId>
17+
<artifactId>maven-compiler-plugin</artifactId>
18+
<version>3.8.0</version>
19+
<configuration>
20+
<release>14</release>
21+
<source>14</source>
22+
<target>14</target>
23+
</configuration>
24+
</plugin>
25+
<plugin>
26+
<groupId>org.openjfx</groupId>
27+
<artifactId>javafx-maven-plugin</artifactId>
28+
<version>0.0.5</version>
29+
<configuration>
30+
<mainClass>com.vimasig.bozar.ui.App</mainClass>
31+
</configuration>
32+
</plugin>
33+
</plugins>
34+
</build>
35+
36+
<dependencies>
37+
<dependency>
38+
<groupId>org.openjfx</groupId>
39+
<artifactId>javafx-controls</artifactId>
40+
<version>13</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.openjfx</groupId>
44+
<artifactId>javafx-fxml</artifactId>
45+
<version>13</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.ow2.asm</groupId>
49+
<artifactId>asm</artifactId>
50+
<version>9.1</version>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.ow2.asm</groupId>
54+
<artifactId>asm-tree</artifactId>
55+
<version>9.1</version>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.ow2.asm</groupId>
59+
<artifactId>asm-commons</artifactId>
60+
<version>9.1</version>
61+
</dependency>
62+
<dependency>
63+
<groupId>com.google.code.gson</groupId>
64+
<artifactId>gson</artifactId>
65+
<version>2.8.6</version>
66+
</dependency>
67+
<dependency>
68+
<groupId>commons-cli</groupId>
69+
<artifactId>commons-cli</artifactId>
70+
<version>1.4</version>
71+
</dependency>
72+
</dependencies>
73+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package com.vimasig.bozar.obfuscator;
2+
3+
import com.vimasig.bozar.obfuscator.transformer.TransformManager;
4+
import com.vimasig.bozar.obfuscator.utils.StreamUtils;
5+
import com.vimasig.bozar.obfuscator.utils.model.BozarConfig;
6+
import com.vimasig.bozar.obfuscator.utils.model.BozarMessage;
7+
import com.vimasig.bozar.obfuscator.utils.model.CustomClassWriter;
8+
import com.vimasig.bozar.obfuscator.utils.model.ResourceWrapper;
9+
import org.objectweb.asm.ClassReader;
10+
import org.objectweb.asm.ClassWriter;
11+
import org.objectweb.asm.tree.ClassNode;
12+
13+
import java.io.ByteArrayInputStream;
14+
import java.io.File;
15+
import java.io.FileNotFoundException;
16+
import java.io.IOException;
17+
import java.net.MalformedURLException;
18+
import java.net.URL;
19+
import java.net.URLClassLoader;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import java.util.jar.JarEntry;
25+
import java.util.jar.JarOutputStream;
26+
import java.util.zip.ZipEntry;
27+
import java.util.zip.ZipInputStream;
28+
29+
public class Bozar implements Runnable {
30+
31+
private final File input;
32+
private final Path output;
33+
private final BozarConfig config;
34+
35+
public Bozar(File input, Path output, BozarConfig config) {
36+
this.input = input;
37+
this.output = output;
38+
this.config = config;
39+
}
40+
41+
private final List<ClassNode> classes = new ArrayList<>();
42+
private final List<ResourceWrapper> resources = new ArrayList<>();
43+
44+
@Override
45+
public void run() {
46+
try {
47+
// Input file checks
48+
if(!this.input.exists())
49+
throw new FileNotFoundException("Cannot find input");
50+
if(!this.input.isFile())
51+
throw new IllegalArgumentException("Received input is not a file");
52+
String inputExtension = this.input.getName().substring(this.input.getName().lastIndexOf(".") + 1).toLowerCase();
53+
switch (inputExtension) {
54+
case "jar" -> {
55+
// Read JAR input
56+
log("Processing JAR input...");
57+
try (var jarInputStream = new ZipInputStream(Files.newInputStream(input.toPath()))) {
58+
ZipEntry zipEntry;
59+
while ((zipEntry = jarInputStream.getNextEntry()) != null) {
60+
if (zipEntry.getName().endsWith(".class")) {
61+
ClassReader reader = new ClassReader(jarInputStream);
62+
ClassNode classNode = new ClassNode();
63+
reader.accept(classNode, 0);
64+
classes.add(classNode);
65+
} else resources.add(new ResourceWrapper(zipEntry, StreamUtils.readAll(jarInputStream)));
66+
}
67+
}
68+
}
69+
default -> throw new IllegalArgumentException("Unsupported file extension: " + inputExtension);
70+
}
71+
72+
// Empty/corrupted file check
73+
if(classes.size() == 0)
74+
throw new IllegalArgumentException("Received input does not look like a proper JAR file");
75+
76+
// Transform
77+
log("Transforming...");
78+
var transformHandler = new TransformManager(this);
79+
transformHandler.transform();
80+
81+
// Write output
82+
log("Writing...");
83+
try (var out = new JarOutputStream(Files.newOutputStream(this.output))) {
84+
// Write resources
85+
resources.forEach(resourceWrapper -> {
86+
try {
87+
out.putNextEntry(new JarEntry(resourceWrapper.getZipEntry().getName()));
88+
StreamUtils.copy(new ByteArrayInputStream(resourceWrapper.getBytes()), out);
89+
} catch (IOException e) {
90+
e.printStackTrace();
91+
}
92+
});
93+
94+
// Convert string library paths to URL array
95+
var libs = this.getConfig().getLibraries();
96+
URL[] urls = new URL[libs.size() + 1];
97+
urls[libs.size()] = this.input.toURI().toURL();
98+
for (int i = 0; i < libs.size(); i++)
99+
urls[i] = new File(libs.get(i)).toURI().toURL();
100+
URLClassLoader classLoader = new URLClassLoader(urls);
101+
102+
// Write classes
103+
for(ClassNode classNode : this.classes) {
104+
var classWriter = new CustomClassWriter(ClassWriter.COMPUTE_FRAMES, classLoader);
105+
classWriter.newUTF8(BozarMessage.WATERMARK.toString());
106+
classNode.accept(classWriter);
107+
byte[] bytes = classWriter.toByteArray();
108+
out.putNextEntry(new JarEntry(classNode.name + ".class"));
109+
out.write(bytes);
110+
}
111+
}
112+
// TODO: Print elapsed time & file bloat
113+
log("Done.");
114+
} catch (IOException e) {
115+
e.printStackTrace();
116+
}
117+
}
118+
119+
public List<ClassNode> getClasses() {
120+
return classes;
121+
}
122+
123+
public List<ResourceWrapper> getResources() {
124+
return resources;
125+
}
126+
127+
public BozarConfig getConfig() {
128+
return config;
129+
}
130+
131+
public void log(String format, Object... args) {
132+
System.out.println("[Bozar] " + String.format(format, args));
133+
}
134+
135+
public void err(String format, Object... args) {
136+
System.out.println("[Bozar] [ERROR] " + String.format(format, args));
137+
}
138+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.vimasig.bozar.obfuscator.transformer;
2+
3+
import com.vimasig.bozar.obfuscator.Bozar;
4+
import org.objectweb.asm.Opcodes;
5+
import org.objectweb.asm.tree.ClassNode;
6+
import org.objectweb.asm.tree.FieldNode;
7+
import org.objectweb.asm.tree.MethodNode;
8+
9+
import java.util.Random;
10+
11+
public class ClassTransformer implements Opcodes {
12+
13+
private final Bozar bozar;
14+
protected final Random random = new Random();
15+
16+
public ClassTransformer(Bozar bozar) {
17+
this.bozar = bozar;
18+
}
19+
20+
public void transformClass(ClassNode classNode) {}
21+
public void transformMethod(ClassNode classNode, MethodNode methodNode) {}
22+
public void transformField(ClassNode classNode, FieldNode fieldNode) {}
23+
24+
public final Bozar getBozar() {
25+
return bozar;
26+
}
27+
28+
public final String getName() {
29+
return this.getClass().getSimpleName();
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.vimasig.bozar.obfuscator.transformer;
2+
3+
import com.vimasig.bozar.obfuscator.Bozar;
4+
import com.vimasig.bozar.obfuscator.transformer.impl.ConstantTransformer;
5+
import com.vimasig.bozar.obfuscator.transformer.impl.LineNumberTransformer;
6+
import com.vimasig.bozar.obfuscator.transformer.impl.LocalVariableTransformer;
7+
import com.vimasig.bozar.obfuscator.transformer.impl.SourceFileTransformer;
8+
import com.vimasig.bozar.obfuscator.utils.ASMUtils;
9+
import org.objectweb.asm.tree.AbstractInsnNode;
10+
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
public class TransformManager {
15+
16+
private final Bozar bozar;
17+
private final List<ClassTransformer> classTransformers = new ArrayList<>();
18+
19+
public TransformManager(Bozar bozar) {
20+
this.bozar = bozar;
21+
this.classTransformers.add(new LocalVariableTransformer(bozar));
22+
this.classTransformers.add(new LineNumberTransformer(bozar));
23+
this.classTransformers.add(new SourceFileTransformer(bozar));
24+
this.classTransformers.add(new ConstantTransformer(bozar));
25+
}
26+
27+
public void transform() {
28+
// TODO: Class version check to skip processing old versions
29+
this.classTransformers.forEach(classTransformer -> {
30+
this.bozar.log("Applying %s", classTransformer.getName());
31+
this.bozar.getClasses().forEach(classNode -> {
32+
// TODO: Exclude feature
33+
classTransformer.transformClass(classNode);
34+
classNode.fields.forEach(fieldNode -> classTransformer.transformField(classNode, fieldNode));
35+
classNode.methods.forEach(methodNode -> {
36+
AbstractInsnNode[] insns = methodNode.instructions.toArray().clone();
37+
classTransformer.transformMethod(classNode, methodNode);
38+
if (!ASMUtils.isMethodSizeValid(methodNode)) {
39+
this.bozar.log("Cannot apply \"%s\" on \"%s\" due to low method capacity", classTransformer.getName(), classNode.name + "." + methodNode.name + methodNode.desc);
40+
methodNode.instructions = ASMUtils.arrayToList(insns);
41+
}
42+
});
43+
});
44+
});
45+
}
46+
}

0 commit comments

Comments
 (0)