├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
├── java
└── dev
│ └── sim0n
│ └── caesium
│ ├── Caesium.java
│ ├── PreRuntime.java
│ ├── Start.java
│ ├── exception
│ └── CaesiumException.java
│ ├── gui
│ ├── CGui.java
│ └── LibraryTab.java
│ ├── manager
│ ├── ClassManager.java
│ └── MutatorManager.java
│ ├── mutator
│ ├── ClassMutator.java
│ └── impl
│ │ ├── AttributeMutator.java
│ │ ├── ClassFolderMutator.java
│ │ ├── ControlFlowMutator.java
│ │ ├── LineNumberMutator.java
│ │ ├── LocalVariableMutator.java
│ │ ├── NumberMutator.java
│ │ ├── OldStringMutator.java
│ │ ├── PolymorphMutator.java
│ │ ├── ReferenceMutator.java
│ │ ├── ShuffleMutator.java
│ │ ├── StringMutator.java
│ │ ├── TrimMutator.java
│ │ └── crasher
│ │ ├── BadAnnotationMutator.java
│ │ └── ImageCrashMutator.java
│ └── util
│ ├── ASMUtil.java
│ ├── ByteUtil.java
│ ├── Dictionary.java
│ ├── OS.java
│ ├── OSUtil.java
│ ├── StringUtil.java
│ ├── VersionUtil.java
│ ├── classwriter
│ ├── CaesiumClassWriter.java
│ └── ClassTree.java
│ ├── trait
│ └── Finishable.java
│ └── wrapper
│ ├── Wrapper.java
│ └── impl
│ ├── ClassWrapper.java
│ ├── FieldWrapper.java
│ └── MethodWrapper.java
└── resources
├── caesium.properties
└── log4j2.xml
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Java CI with Maven
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v2
19 | - name: Set up JDK 1.8
20 | uses: actions/setup-java@v1
21 | with:
22 | java-version: 1.8
23 | - name: Build with Maven
24 | run: mvn -B package --file pom.xml
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Java template
3 | # Compiled class file
4 | *.class
5 |
6 | # Log file
7 | *.log
8 |
9 | # BlueJ files
10 | *.ctxt
11 |
12 | # Mobile Tools for Java (J2ME)
13 | .mtj.tmp/
14 |
15 | # Package Files #
16 | *.jar
17 | *.war
18 | *.nar
19 | *.ear
20 | *.zip
21 | *.tar.gz
22 | *.rar
23 |
24 | *.txt
25 |
26 | # IntelliJ
27 | *.iml
28 | .idea
29 | target
30 |
31 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
32 | hs_err_pid*
33 | .classpath
34 | .project
35 | .settings/org.eclipse.jdt.core.prefs
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 sim0n
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Caesium
2 | Caesium is a powerful Java bytecode obfuscator written by [sim0n](https://github.com/sim0n) for fun, and released for the public.
3 |
4 | 
5 |
6 | ### Currently available mutators
7 | * Class Folder (Turns classes into folders)
8 | * Control Flow
9 | * Crasher (Will crash almost every GUI based RE tool)
10 | * Local Variable
11 | * Line Number
12 | * Number
13 | * Polymorph
14 | * Reference (invokedynamics)
15 | * String
16 | * Trim (Currently only trims math functions)
17 |
18 | ## Notes
19 | You have to add every dependency your jar relies on.
20 | Caesium is very optimised and the performance loss shouldn't be more than 5-10% (unless you're using reference mutation)
21 |
22 | ## Usage
23 | - Run the jar.
24 | - Select mutators in the mutators tab.
25 | - Hit mutate. Done!
26 |
27 | ## Community
28 | If you want to join the discord for Caesium to talk, ask questions or anything then feel free to join [the discord](https://discord.gg/kxC2FYMfNZ)
29 |
30 | ## Special thanks to
31 | 
32 |
33 | [YourKit](https://www.yourkit.com/) is the creator of YourKit Java Profiler, YourKit .NET Profiler, and YourKit YouMonitor. They support open source projects with their fully featured application profilers. It's used to ensure that this project will be as fast as possible.
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | dev.sim0n
8 | caesium
9 | 1.0.9
10 |
11 |
12 |
13 |
14 | org.apache.maven.plugins
15 | maven-compiler-plugin
16 |
17 | 8
18 | 8
19 |
20 |
21 |
22 | org.apache.maven.plugins
23 | maven-jar-plugin
24 |
25 |
26 |
27 | dev.sim0n.caesium.gui.CGui
28 |
29 |
30 |
31 |
32 |
33 | org.apache.maven.plugins
34 | maven-shade-plugin
35 | 3.2.2
36 |
37 | false
38 |
39 |
40 |
41 | package
42 |
43 | shade
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | src/main/resources
53 | true
54 |
55 |
56 |
57 |
58 |
59 |
60 | org.projectlombok
61 | lombok
62 | 1.18.12
63 | provided
64 |
65 |
66 | net.sf.jopt-simple
67 | jopt-simple
68 | 5.0.4
69 |
70 |
71 | org.apache.logging.log4j
72 | log4j-core
73 | 2.13.1
74 |
75 |
76 | org.ow2.asm
77 | asm-tree
78 | 7.3.1
79 |
80 |
81 | org.ow2.asm
82 | asm-commons
83 | 7.3.1
84 |
85 |
86 | com.google.guava
87 | guava
88 | 28.2-jre
89 |
90 |
91 | org.junit.jupiter
92 | junit-jupiter
93 | RELEASE
94 | test
95 |
96 |
97 | com.formdev
98 | flatlaf
99 | 0.44
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/Caesium.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium;
2 |
3 | import com.google.common.base.Strings;
4 | import dev.sim0n.caesium.manager.ClassManager;
5 | import dev.sim0n.caesium.manager.MutatorManager;
6 | import dev.sim0n.caesium.util.ByteUtil;
7 | import dev.sim0n.caesium.util.Dictionary;
8 | import dev.sim0n.caesium.util.VersionUtil;
9 | import lombok.Getter;
10 | import lombok.Setter;
11 | import org.apache.logging.log4j.LogManager;
12 | import org.apache.logging.log4j.Logger;
13 |
14 | import java.io.File;
15 | import java.io.PrintStream;
16 | import java.security.SecureRandom;
17 | import java.util.Optional;
18 |
19 | import static com.google.common.base.Preconditions.checkNotNull;
20 |
21 | @Getter
22 | public class Caesium {
23 | public static final String VERSION = VersionUtil.getVersion();
24 |
25 | private static final String SEPARATOR = Strings.repeat("-", 30);
26 |
27 | @Getter
28 | private static final Logger logger = LogManager.getLogger();
29 |
30 | private final SecureRandom random = new SecureRandom();
31 |
32 | private static Optional instance;
33 |
34 | private final MutatorManager mutatorManager;
35 | private final ClassManager classManager;
36 |
37 | @Setter
38 | private Dictionary dictionary = Dictionary.NUMBERS;
39 |
40 | public Caesium() {
41 | instance = Optional.of(this);
42 |
43 | mutatorManager = new MutatorManager();
44 | classManager = new ClassManager();
45 | }
46 |
47 | public int run(File input, File output) throws Exception {
48 | checkNotNull(input, "Input can't be null");
49 | checkNotNull(output, "Output can't be null");
50 |
51 | separator();
52 | logger.info(String.format("Caesium version %s", VERSION));
53 | separator();
54 |
55 | classManager.parseJar(input);
56 | classManager.handleMutation();
57 | classManager.exportJar(output);
58 |
59 | double inputKB = ByteUtil.bytesToKB(input.length());
60 | double outputKB = ByteUtil.bytesToKB(output.length());
61 |
62 | logger.info(String.format("Successfully obfuscated target jar. %.3fkb -> %.3fkb", inputKB, outputKB));
63 |
64 | return 0;
65 | }
66 |
67 | public void separator() {
68 | logger.info(SEPARATOR);
69 | }
70 |
71 | public static Caesium getInstance() {
72 | return instance.orElseThrow(() -> new IllegalStateException("Caesium instance is null"));
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/PreRuntime.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium;
2 |
3 | import java.awt.HeadlessException;
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.Enumeration;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 | import java.util.zip.ZipEntry;
12 | import java.util.zip.ZipException;
13 | import java.util.zip.ZipFile;
14 |
15 | import javax.swing.DefaultListModel;
16 | import javax.swing.JOptionPane;
17 |
18 | import org.objectweb.asm.ClassReader;
19 | import org.objectweb.asm.tree.ClassNode;
20 |
21 | import dev.sim0n.caesium.exception.CaesiumException;
22 | import dev.sim0n.caesium.gui.LibraryTab;
23 | import dev.sim0n.caesium.util.OSUtil;
24 | import dev.sim0n.caesium.util.classwriter.ClassTree;
25 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
26 |
27 | public class PreRuntime {
28 | private static Map classPath = new HashMap<>();;
29 | private static Map classes = new HashMap<>();
30 | private static Map hierarchy = new HashMap<>();
31 | public static DefaultListModel libraries = new DefaultListModel<>();
32 |
33 | public static void loadJavaRuntime() throws HeadlessException, IOException {
34 | String path;
35 | switch (OSUtil.getCurrentOS()) {
36 | case WINDOWS:
37 | path = System.getProperty("sun.boot.class.path");
38 | if (path != null) {
39 | String[] pathFiles = path.split(";");
40 | for (String lib : pathFiles) {
41 | if (lib.endsWith(".jar")) {
42 | libraries.addElement(lib);
43 | }
44 | }
45 | } else {
46 | JOptionPane.showMessageDialog(null, "rt.jar was not found, you need to add it manually.",
47 | "Runtime Error", JOptionPane.ERROR_MESSAGE);
48 | }
49 | break;
50 |
51 | case UNIX:
52 | case MAC:
53 | path = System.getProperty("sun.boot.class.path");
54 | if (path != null) {
55 | String[] pathFiles = path.split(":");
56 | for (String lib : pathFiles) {
57 | if (lib.endsWith(".jar")) {
58 | libraries.addElement(lib);
59 | }
60 | }
61 | } else {
62 | JOptionPane.showMessageDialog(null, "rt.jar was not found, you need to add it manually.",
63 | "Runtime Error", JOptionPane.ERROR_MESSAGE);
64 | }
65 | break;
66 | default:
67 | break;
68 | }
69 | }
70 |
71 | public static void loadInput(String inputFile) throws CaesiumException {
72 | File input = new File(inputFile);
73 | if (input.exists()) {
74 | // Logger.info(String.format("Loading input \"%s\".", input.getAbsolutePath()));
75 | try {
76 | ZipFile zipFile = new ZipFile(input);
77 | Enumeration extends ZipEntry> entries = zipFile.entries();
78 | while (entries.hasMoreElements()) {
79 | ZipEntry entry = entries.nextElement();
80 | if (!entry.isDirectory()) {
81 | if (entry.getName().endsWith(".class")) {
82 | try {
83 | ClassReader cr = new ClassReader(zipFile.getInputStream(entry));
84 | ClassNode classNode = new ClassNode();
85 | cr.accept(classNode, ClassReader.SKIP_FRAMES);
86 | ClassWrapper classWrapper = new ClassWrapper(classNode, false);
87 | classPath.put(classWrapper.originalName, classWrapper);
88 | classes.put(classWrapper.originalName, classWrapper);
89 | } catch (Throwable ignored) {
90 |
91 | }
92 | }
93 | }
94 | }
95 | zipFile.close();
96 | } catch (ZipException e) {
97 |
98 | throw new CaesiumException(
99 | String.format("Input file \"%s\" could not be opened as a zip file.", input.getAbsolutePath()),
100 | e);
101 | } catch (IOException e) {
102 | throw new CaesiumException(String.format(
103 | "IOException happened while trying to load classes from \"%s\".", input.getAbsolutePath()), e);
104 | }
105 | } else {
106 | throw new CaesiumException(String.format("Unable to find \"%s\".", input.getAbsolutePath()), null);
107 | }
108 | }
109 |
110 | public static void loadClassPath() {
111 | ArrayList libs;
112 |
113 | libs = Collections.list(libraries.elements());
114 | for (String s : libs) {
115 | File file = new File(s);
116 | if (file.exists()) {
117 | System.out.printf("Loading library \"%s\".%n", file.getAbsolutePath());
118 | try {
119 | ZipFile zipFile = new ZipFile(file);
120 | Enumeration extends ZipEntry> entries = zipFile.entries();
121 | while (entries.hasMoreElements()) {
122 | ZipEntry entry = entries.nextElement();
123 | if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
124 | try {
125 | ClassReader cr = new ClassReader(zipFile.getInputStream(entry));
126 | ClassNode classNode = new ClassNode();
127 | cr.accept(classNode,
128 | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
129 | ClassWrapper classWrapper = new ClassWrapper(classNode, true);
130 |
131 | classPath.put(classWrapper.originalName, classWrapper);
132 | } catch (Throwable t) {
133 | // Don't care.
134 | }
135 | }
136 | }
137 | zipFile.close();
138 | } catch (ZipException e) {
139 | // Logger.info(
140 | // String.format("Library \"%s\" could not be opened as a zip file.",
141 | // file.getAbsolutePath()));
142 | e.printStackTrace();
143 | } catch (IOException e) {
144 | // Logger.info(String.format("IOException happened while trying to load classes
145 | // from \"%s\".",
146 | // file.getAbsolutePath()));
147 | e.printStackTrace();
148 | }
149 | } else {
150 | // Logger.info(String.format("Library \"%s\" could not be found and will be
151 | // ignored.",
152 | // file.getAbsolutePath()));
153 | }
154 |
155 | }
156 | }
157 |
158 | public static void buildHierarchy(ClassWrapper classWrapper, ClassWrapper sub) throws CaesiumException {
159 | if (hierarchy.get(classWrapper.node.name) == null) {
160 | ClassTree tree = new ClassTree(classWrapper);
161 | if (classWrapper.node.superName != null) {
162 | tree.parentClasses.add(classWrapper.node.superName);
163 | ClassWrapper superClass = classPath.get(classWrapper.node.superName);
164 | if (superClass == null)
165 | throw new CaesiumException(classWrapper.node.superName + " is missing in the classpath.", null);
166 | buildHierarchy(superClass, classWrapper);
167 | }
168 | if (classWrapper.node.interfaces != null && !classWrapper.node.interfaces.isEmpty()) {
169 | for (String s : classWrapper.node.interfaces) {
170 | tree.parentClasses.add(s);
171 | ClassWrapper interfaceClass = classPath.get(s);
172 | if (interfaceClass == null)
173 | throw new CaesiumException(s + " is missing in the classpath.", null);
174 |
175 | buildHierarchy(interfaceClass, classWrapper);
176 | }
177 | }
178 | hierarchy.put(classWrapper.node.name, tree);
179 | }
180 | if (sub != null) {
181 | hierarchy.get(classWrapper.node.name).subClasses.add(sub.node.name);
182 | }
183 | }
184 |
185 | public static void buildInheritance() {
186 | classes.values().forEach(classWrapper -> {
187 | try {
188 | buildHierarchy(classWrapper, null);
189 | } catch (CaesiumException e) {
190 | // TODO Auto-generated catch block
191 | e.printStackTrace();
192 | }
193 | });
194 | }
195 |
196 | public static Map getClassPath() {
197 | return classPath;
198 | }
199 |
200 | public static Map getClasses() {
201 | return classes;
202 | }
203 |
204 | public static Map getHierarchy() {
205 | return hierarchy;
206 | }
207 |
208 | }
209 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/Start.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import joptsimple.OptionParser;
5 | import joptsimple.OptionSet;
6 | import joptsimple.OptionSpec;
7 |
8 | import java.io.File;
9 |
10 | public class Start {
11 | public static void main(String[] args) {
12 | OptionParser optionParser = new OptionParser();
13 | OptionSpec help = optionParser
14 | .acceptsAll(ImmutableList.of("H", "help"), "show help menu")
15 | .forHelp();
16 |
17 | OptionSpec input = optionParser
18 | .acceptsAll(ImmutableList.of("I", "input"), "the path of the input JAR file")
19 | .withRequiredArg()
20 | .required()
21 | .ofType(File.class);
22 |
23 | try {
24 | OptionSet options = optionParser.parse(args);
25 |
26 | if (options.has(help)) {
27 | optionParser.printHelpOn(System.out);
28 | return;
29 | }
30 |
31 | File inputFile = input.value(options);
32 | File outputFile = new File(inputFile.getName().replace(".jar", "-mutated.jar"));
33 |
34 | Caesium caesium = new Caesium();
35 |
36 | if (caesium.run(inputFile, outputFile) != 0) {
37 | Caesium.getLogger().warn("Exited with non default exit code.");
38 | }
39 | } catch (Exception e) {
40 | e.printStackTrace();
41 | System.exit(-1);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/exception/CaesiumException.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.exception;
2 |
3 | public class CaesiumException extends Exception {
4 |
5 | public CaesiumException(String message, Throwable cause) {
6 | super(message, cause);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/gui/CGui.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Created by JFormDesigner on Thu Nov 19 22:55:38 CET 2020
3 | */
4 |
5 | package dev.sim0n.caesium.gui;
6 |
7 | import com.formdev.flatlaf.FlatLightLaf;
8 | import dev.sim0n.caesium.Caesium;
9 | import dev.sim0n.caesium.PreRuntime;
10 | import dev.sim0n.caesium.exception.CaesiumException;
11 | import dev.sim0n.caesium.manager.MutatorManager;
12 | import dev.sim0n.caesium.mutator.impl.*;
13 | import dev.sim0n.caesium.mutator.impl.crasher.BadAnnotationMutator;
14 | import dev.sim0n.caesium.mutator.impl.crasher.ImageCrashMutator;
15 | import dev.sim0n.caesium.util.Dictionary;
16 |
17 | import java.awt.*;
18 | import java.io.File;
19 | import java.io.IOException;
20 | import java.util.Enumeration;
21 |
22 | import javax.swing.*;
23 | import javax.swing.filechooser.FileFilter;
24 | import javax.swing.filechooser.FileNameExtensionFilter;
25 |
26 | /**
27 | * This entire thing is a mess because it was automatically generated with
28 | * JFormDesigner
29 | */
30 |
31 | public class CGui extends JFrame {
32 |
33 | public CGui() {
34 | FlatLightLaf.install();
35 |
36 | setResizable(false);
37 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
38 | initComponents();
39 | }
40 |
41 | public static void main(String[] args) throws HeadlessException, IOException {
42 |
43 | PreRuntime.loadJavaRuntime();
44 |
45 | new CGui().setVisible(true);
46 | }
47 |
48 | private DefaultListModel listModel = new DefaultListModel<>();
49 |
50 | private void initComponents() {
51 | // JFormDesigner - Component initialization - DO NOT MODIFY
52 | // //GEN-BEGIN:initComponents
53 | // Generated using JFormDesigner Evaluation license - unknown
54 | tabbedPane1 = new JTabbedPane();
55 | panel1 = new JPanel();
56 | label1 = new JLabel();
57 | label2 = new JLabel();
58 | textField1 = new JTextField();
59 | textField2 = new JTextField();
60 | button1 = new JButton();
61 | button2 = new JButton();
62 | label3 = new JLabel();
63 | comboBox1 = new JComboBox();
64 | panel2 = new JPanel();
65 | checkBox1 = new JCheckBox();
66 | checkBox2 = new JCheckBox();
67 | label4 = new JLabel();
68 | comboBox2 = new JComboBox();
69 | checkBox3 = new JCheckBox();
70 | label5 = new JLabel();
71 | comboBox3 = new JComboBox();
72 | checkBox4 = new JCheckBox();
73 | checkBox5 = new JCheckBox();
74 | checkBox6 = new JCheckBox();
75 | checkBox7 = new JCheckBox();
76 | checkBox8 = new JCheckBox();
77 | label6 = new JLabel();
78 | comboBox4 = new JComboBox();
79 | panel3 = new JPanel();
80 | tabbedPane2 = new JTabbedPane();
81 | panel5 = new JPanel();
82 | scrollPane1 = new JScrollPane();
83 | list1 = new JList();
84 | button4 = new JButton();
85 | textField3 = new JTextField();
86 | button5 = new JButton();
87 | panel6 = new JPanel();
88 | panel4 = new JPanel();
89 | panel7 = new JPanel();
90 | label7 = new JLabel();
91 | comboBox5 = new JComboBox();
92 | button3 = new JButton();
93 |
94 | list1.setModel(listModel);
95 |
96 | // ======== this ========
97 | setTitle("Caesium Obfuscator");
98 | Container contentPane = getContentPane();
99 | contentPane.setLayout(null);
100 |
101 | // ======== tabbedPane1 ========
102 | {
103 | tabbedPane1.setFocusable(false);
104 |
105 | // ======== panel1 ========
106 | {
107 | panel1.setLayout(null);
108 |
109 | // ---- label1 ----
110 | label1.setText("Output");
111 | panel1.add(label1);
112 | label1.setBounds(new Rectangle(new Point(5, 45), label1.getPreferredSize()));
113 |
114 | // ---- label2 ----
115 | label2.setText("Input");
116 | panel1.add(label2);
117 | label2.setBounds(new Rectangle(new Point(5, 15), label2.getPreferredSize()));
118 | panel1.add(textField1);
119 | textField1.setBounds(55, 15, 220, textField1.getPreferredSize().height);
120 | panel1.add(textField2);
121 | textField2.setBounds(55, 45, 220, textField2.getPreferredSize().height);
122 |
123 | // ---- button1 ----
124 | button1.setText("...");
125 | button1.setFocusable(false);
126 | panel1.add(button1);
127 | button1.addActionListener(l -> {
128 | JFileChooser chooser = new JFileChooser(".");
129 |
130 | FileFilter jarFileFilter = new FileNameExtensionFilter("Jar Files", "jar");
131 |
132 | chooser.setFileFilter(jarFileFilter);
133 | chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
134 | int response = chooser.showOpenDialog(button1);
135 |
136 | if (response == JFileChooser.APPROVE_OPTION) {
137 | File file = chooser.getSelectedFile();
138 |
139 | textField1.setText(file.getAbsolutePath());
140 | }
141 | });
142 | button1.setBounds(285, 15, 50, 22);
143 |
144 | // ---- button2 ----
145 | button2.setText("...");
146 | button2.setFocusable(false);
147 | button2.addActionListener(l -> {
148 | JFileChooser chooser = new JFileChooser(".");
149 |
150 | int response = chooser.showOpenDialog(button2);
151 |
152 | if (response == JFileChooser.APPROVE_OPTION) {
153 | File file = chooser.getSelectedFile();
154 |
155 | textField2.setText(file.getAbsolutePath());
156 | }
157 | });
158 | panel1.add(button2);
159 | button2.setBounds(285, 45, 50, 22);
160 |
161 | // ---- label3 ----
162 | label3.setText("Application type");
163 | panel1.add(label3);
164 | label3.setBounds(new Rectangle(new Point(10, 88), label3.getPreferredSize()));
165 | panel1.add(comboBox1);
166 | comboBox1.addItem("Self contained");
167 | comboBox1.addItem("Spigot plugin");
168 |
169 | comboBox1.setFocusable(false);
170 | comboBox1.setBounds(140, 85, 170, comboBox1.getPreferredSize().height);
171 | }
172 | tabbedPane1.addTab("Main", panel1);
173 |
174 | // ======== panel2 ========
175 | {
176 | panel2.setLayout(null);
177 |
178 | // ---- checkBox1 ----
179 | checkBox1.setText("String literal mutation");
180 | checkBox1.setFocusable(false);
181 | panel2.add(checkBox1);
182 | checkBox1.setBounds(new Rectangle(new Point(10, 10), checkBox1.getPreferredSize()));
183 |
184 | // ---- checkBox2 ----
185 | checkBox2.setText("Control flow mutation");
186 | checkBox2.setFocusable(false);
187 | panel2.add(checkBox2);
188 | checkBox2.setBounds(new Rectangle(new Point(10, 40), checkBox2.getPreferredSize()));
189 |
190 | // ---- label4 ----
191 | label4.setText("Reference Mutation");
192 | panel2.add(label4);
193 | label4.setBounds(new Rectangle(new Point(15, 113), label4.getPreferredSize()));
194 | comboBox2.addItem("Off");
195 | comboBox2.addItem("Light");
196 | comboBox2.addItem("Normal");
197 | comboBox2.setFocusable(false);
198 | panel2.add(comboBox2);
199 | comboBox2.setBounds(150, 110, 170, comboBox2.getPreferredSize().height);
200 |
201 | // ---- checkBox3 ----
202 | checkBox3.setText("Number mutation");
203 | checkBox3.setFocusable(false);
204 | panel2.add(checkBox3);
205 | checkBox3.setBounds(new Rectangle(new Point(10, 70), checkBox3.getPreferredSize()));
206 |
207 | // ---- label5 ----
208 | label5.setText("Local Variable tables");
209 | panel2.add(label5);
210 | label5.setBounds(new Rectangle(new Point(15, 158), label5.getPreferredSize()));
211 | comboBox3.addItem("Off");
212 | comboBox3.addItem("Remove");
213 | comboBox3.addItem("Rename");
214 | comboBox3.setFocusable(false);
215 | panel2.add(comboBox3);
216 | comboBox3.setBounds(150, 155, 170, comboBox3.getPreferredSize().height);
217 |
218 | checkBox6.setText("Polymorph");
219 | checkBox6.setFocusable(false);
220 | panel2.add(checkBox6);
221 | checkBox6.setBounds(new Rectangle(new Point(10, 240), checkBox6.getPreferredSize()));
222 | // ---- checkBox4 ----
223 | checkBox4.setText("Crasher");
224 | checkBox4.setFocusable(false);
225 | panel2.add(checkBox4);
226 | checkBox4.setBounds(new Rectangle(new Point(10, 270), checkBox4.getPreferredSize()));
227 |
228 | // ---- checkBox5 ----
229 | checkBox5.setText("Class Folder");
230 | checkBox5.setFocusable(false);
231 | panel2.add(checkBox5);
232 | checkBox5.setBounds(new Rectangle(new Point(90, 270), checkBox5.getPreferredSize()));
233 |
234 | // ---- checkBox7 ----
235 | checkBox7.setText("Trimmer");
236 | checkBox7.setFocusable(false);
237 | panel2.add(checkBox7);
238 | checkBox7.setBounds(new Rectangle(new Point(10, 300), checkBox7.getPreferredSize()));
239 |
240 | // ---- checkBox8 ----
241 | checkBox8.setText("Shuffle Members");
242 | checkBox8.setFocusable(false);
243 | panel2.add(checkBox8);
244 | checkBox8.setBounds(new Rectangle(new Point(10, 330), checkBox8.getPreferredSize()));
245 |
246 | // ---- label6 ----
247 | label6.setText("Line Number tables");
248 | panel2.add(label6);
249 | label6.setBounds(new Rectangle(new Point(15, 203), label6.getPreferredSize()));
250 | comboBox4.setFocusable(false);
251 | comboBox4.addItem("Off");
252 | comboBox4.addItem("Remove");
253 | comboBox4.addItem("Rename");
254 | panel2.add(comboBox4);
255 | comboBox4.setBounds(150, 200, 170, comboBox4.getPreferredSize().height);
256 | }
257 | tabbedPane1.addTab("Mutators", panel2);
258 |
259 | // ======== panel3 ========
260 | {
261 | panel3.setLayout(null);
262 |
263 | // ======== tabbedPane2 ========
264 | {
265 | tabbedPane2.setFocusable(false);
266 |
267 | // ======== panel5 ========
268 | {
269 | panel5.setLayout(null);
270 |
271 | // ======== scrollPane1 ========
272 | {
273 | scrollPane1.setViewportView(list1);
274 | }
275 | panel5.add(scrollPane1);
276 | scrollPane1.setBounds(1, 5, 324, 275);
277 |
278 | // ---- button4 ----
279 | button4.setText("Add");
280 | button4.setFocusable(false);
281 | panel5.add(button4);
282 | button4.setBounds(178, 285, 58, button4.getPreferredSize().height);
283 |
284 | // ---- textField3 ----
285 | textField3.setText("");
286 | panel5.add(textField3);
287 | textField3.setBounds(2, 285, 173, 22);
288 |
289 | button4.addActionListener(l -> {
290 | if (textField3.getText().length() > 0) {
291 | listModel.addElement(textField3.getText());
292 | textField3.setText("");
293 | }
294 | });
295 |
296 | // ---- button5 ----
297 | button5.setText("Remove");
298 | button5.setFocusable(false);
299 | panel5.add(button5);
300 |
301 | button5.addActionListener(l -> {
302 | if (listModel.size() > 0 && list1.getSelectedIndex() != -1) {
303 | listModel.removeElementAt(list1.getSelectedIndex());
304 | }
305 | });
306 | button5.setBounds(238, 285, 85, button5.getPreferredSize().height);
307 | }
308 | tabbedPane2.addTab("Strings", panel5);
309 |
310 | // ======== panel6 ========
311 | {
312 | panel6.setLayout(null);
313 | }
314 | tabbedPane2.addTab("References", panel6);
315 | }
316 | panel3.add(tabbedPane2);
317 | tabbedPane2.setBounds(5, 0, 330, 350);
318 | }
319 | tabbedPane1.addTab("Exclusions", panel3);
320 |
321 | // ======== panel4 ========
322 | {
323 | // panel4.setLayout(null);
324 |
325 | // ---- label7 ----
326 | label7.setText("Dictionary");
327 | panel1.add(label7);
328 | label7.setBounds(new Rectangle(new Point(10, 125), label7.getPreferredSize()));
329 | comboBox5.addItem("abc");
330 | comboBox5.addItem("ABC");
331 | comboBox5.addItem("III");
332 | comboBox5.addItem("123");
333 | comboBox5.addItem("Wack");
334 | comboBox5.setSelectedIndex(3);
335 | comboBox5.setFocusable(false);
336 |
337 | panel1.add(comboBox5);
338 | comboBox5.setBounds(140, 125, 150, comboBox5.getPreferredSize().height);
339 | }
340 | // tabbedPane1.addTab("Settings", panel4);
341 | // ======== panel5 ========
342 | {
343 | LibraryTab libraryTab = new LibraryTab();
344 | tabbedPane1.addTab("Dependencies", libraryTab);
345 | //comboBox5.setBounds(75, 15, 150, comboBox5.getPreferredSize().height);
346 | }
347 | }
348 | contentPane.add(tabbedPane1);
349 | tabbedPane1.setBounds(5, 5, 425, 390);
350 |
351 | // ---- button3 ----
352 | button3.setText("Mutate");
353 | button3.addActionListener(l -> {
354 | Caesium caesium = new Caesium();
355 |
356 | File input = new File(textField1.getText());
357 |
358 | try {
359 | PreRuntime.loadInput(textField1.getText());
360 | } catch (CaesiumException e1) {
361 | // TODO Auto-generated catch block
362 | e1.printStackTrace();
363 | }
364 | PreRuntime.loadClassPath();
365 | PreRuntime.buildInheritance();
366 | if (!input.exists()) {
367 | JOptionPane.showMessageDialog(null, "Unable to find input file", "", JOptionPane.WARNING_MESSAGE);
368 | return;
369 | }
370 |
371 | File parent = new File(input.getParent());
372 |
373 | File output = new File(textField2.getText());
374 |
375 | if (output.exists()) {
376 | // we do it this way so we don't have to loop through a specified x amount of
377 | // times
378 | for (int i = 0; i < parent.listFiles().length; i++) {
379 | String filePath = String.format("%s.BACKUP-%d", output.getAbsoluteFile(), i);
380 | File file = new File(filePath);
381 |
382 | if (!file.exists() && output.renameTo(new File(filePath))) {
383 | output = new File(textField2.getText());
384 | break;
385 | }
386 | }
387 | }
388 |
389 | try {
390 | caesium.setDictionary(Dictionary.values()[comboBox5.getSelectedIndex()]);
391 |
392 | MutatorManager mutatorManager = caesium.getMutatorManager();
393 |
394 | // string
395 | StringMutator stringMutator = mutatorManager.getMutator(StringMutator.class);
396 |
397 | stringMutator.setEnabled(checkBox1.isSelected());
398 |
399 | Enumeration elements = listModel.elements();
400 |
401 | while (elements.hasMoreElements()) {
402 | stringMutator.getExclusions().add(elements.nextElement());
403 | }
404 |
405 | mutatorManager.getMutator(BadAnnotationMutator.class).setEnabled(checkBox4.isSelected());
406 |
407 | mutatorManager.getMutator(ControlFlowMutator.class).setEnabled(checkBox2.isSelected());
408 | mutatorManager.getMutator(NumberMutator.class).setEnabled(checkBox3.isSelected());
409 |
410 | mutatorManager.getMutator(PolymorphMutator.class).setEnabled(checkBox6.isSelected());
411 |
412 | mutatorManager.getMutator(ImageCrashMutator.class).setEnabled(checkBox4.isSelected());
413 | mutatorManager.getMutator(ClassFolderMutator.class).setEnabled(checkBox5.isSelected());
414 |
415 | mutatorManager.getMutator(TrimMutator.class).setEnabled(checkBox7.isSelected());
416 | mutatorManager.getMutator(ShuffleMutator.class).setEnabled(checkBox8.isSelected());
417 |
418 | {
419 | int value = comboBox2.getSelectedIndex();
420 |
421 | if (value > 0) {
422 | ReferenceMutator mutator = mutatorManager.getMutator(ReferenceMutator.class);
423 |
424 | mutator.setEnabled(true);
425 | }
426 | }
427 |
428 | {
429 | int value = comboBox4.getSelectedIndex();
430 |
431 | if (value > 0) {
432 | LineNumberMutator mutator = mutatorManager.getMutator(LineNumberMutator.class);
433 |
434 | mutator.setType(value - 1);
435 |
436 | mutator.setEnabled(true);
437 | }
438 | }
439 |
440 | {
441 | int value = comboBox3.getSelectedIndex();
442 |
443 | if (value > 0) {
444 | LocalVariableMutator mutator = mutatorManager.getMutator(LocalVariableMutator.class);
445 |
446 | mutator.setType(value - 1);
447 |
448 | mutator.setEnabled(true);
449 | }
450 | }
451 |
452 | if (caesium.run(input, output) != 0) {
453 | Caesium.getLogger().warn("Exited with non default exit code.");
454 | }
455 | } catch (Exception e) {
456 | e.printStackTrace();
457 | }
458 | });
459 | contentPane.add(button3);
460 | button3.setBounds(new Rectangle(new Point(277, 400), button3.getPreferredSize()));
461 |
462 | contentPane.setPreferredSize(new Dimension(355, 430));
463 | pack();
464 | setLocationRelativeTo(getOwner());
465 | // JFormDesigner - End of component initialization //GEN-END:initComponents
466 | }
467 |
468 | // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
469 | // Generated using JFormDesigner Evaluation license - unknown
470 | private JTabbedPane tabbedPane1;
471 | private JPanel panel1;
472 | private JLabel label1;
473 | private JLabel label2;
474 | private JTextField textField1;
475 | private JTextField textField2;
476 | private JButton button1;
477 | private JButton button2;
478 | private JLabel label3;
479 | private JComboBox comboBox1;
480 | private JPanel panel2;
481 | private JCheckBox checkBox1;
482 | private JCheckBox checkBox2;
483 | private JLabel label4;
484 | private JComboBox comboBox2;
485 | private JCheckBox checkBox3;
486 | private JLabel label5;
487 | private JComboBox comboBox3;
488 | private JCheckBox checkBox4;
489 | private JCheckBox checkBox5;
490 | private JCheckBox checkBox6;
491 | private JCheckBox checkBox7;
492 | private JCheckBox checkBox8;
493 | private JLabel label6;
494 | private JComboBox comboBox4;
495 | private JPanel panel3;
496 | private JTabbedPane tabbedPane2;
497 | private JPanel panel5;
498 | private JPanel panel7;
499 | private JScrollPane scrollPane1;
500 | private JList list1;
501 | private JButton button4;
502 | private JTextField textField3;
503 | private JButton button5;
504 | private JPanel panel6;
505 | private JPanel panel4;
506 | private JLabel label7;
507 | private JComboBox comboBox5;
508 | private JButton button3;
509 | // JFormDesigner - End of variables declaration //GEN-END:variables
510 | }
511 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/gui/LibraryTab.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.gui;
2 |
3 | import javax.swing.*;
4 | import javax.swing.filechooser.FileNameExtensionFilter;
5 |
6 | import dev.sim0n.caesium.PreRuntime;
7 |
8 | import java.awt.*;
9 | import java.io.File;
10 | import java.util.List;
11 |
12 | public class LibraryTab extends JPanel {
13 | /**
14 | *
15 | */
16 | private static final long serialVersionUID = 4696970146752109196L;
17 |
18 | public LibraryTab() {
19 | GridBagLayout gbl_this = new GridBagLayout();
20 | gbl_this.columnWidths = new int[] { 0, 0, 0, 0, 0 };
21 | gbl_this.rowHeights = new int[] { 0, 0, 0 };
22 | gbl_this.columnWeights = new double[] { 0.0, 1.0, 0.0, 0.0, Double.MIN_VALUE };
23 | gbl_this.rowWeights = new double[] { 1.0, 0.0, Double.MIN_VALUE };
24 | this.setLayout(gbl_this);
25 |
26 | JScrollPane ScrollPane = new JScrollPane();
27 | GridBagConstraints gbc_ScrollPane = new GridBagConstraints();
28 | gbc_ScrollPane.gridwidth = 4;
29 | gbc_ScrollPane.insets = new Insets(5, 5, 5, 5);
30 | gbc_ScrollPane.fill = GridBagConstraints.BOTH;
31 | gbc_ScrollPane.gridx = 0;
32 | gbc_ScrollPane.gridy = 0;
33 | this.add(ScrollPane, gbc_ScrollPane);
34 |
35 | JList libList = new JList<>(PreRuntime.libraries);
36 | ScrollPane.setViewportView(libList);
37 |
38 | JTextField librariesField = new JTextField();
39 | GridBagConstraints gbc_librariesField = new GridBagConstraints();
40 | gbc_librariesField.insets = new Insets(0, 0, 5, 5);
41 | gbc_librariesField.fill = GridBagConstraints.HORIZONTAL;
42 | gbc_librariesField.gridx = 1;
43 | gbc_librariesField.gridy = 1;
44 | this.add(librariesField, gbc_librariesField);
45 | librariesField.setColumns(10);
46 |
47 | JButton addButton = new JButton("Add");
48 | GridBagConstraints gbc_addButton = new GridBagConstraints();
49 | gbc_addButton.insets = new Insets(0, 0, 5, 5);
50 | gbc_addButton.gridx = 2;
51 | gbc_addButton.gridy = 1;
52 | addButton.addActionListener((e) -> {
53 | if (librariesField.getText().length() > 1) {
54 | String path = librariesField.getText();
55 |
56 | File file = new File(path);
57 |
58 | if (file.exists()) {
59 | PreRuntime.libraries.addElement(path);
60 | librariesField.setText(null);
61 | } else {
62 | JOptionPane.showMessageDialog(null, "Could not locate dependency.", "Caesium Dependency",
63 | JOptionPane.ERROR_MESSAGE);
64 | }
65 | } else {
66 | final JFileChooser chooser = new JFileChooser();
67 | final FileNameExtensionFilter filter = new FileNameExtensionFilter("Java File", "jar");
68 | chooser.setFileFilter(filter);
69 | chooser.setMultiSelectionEnabled(true);
70 | chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
71 |
72 | int result = chooser.showOpenDialog(librariesField);
73 |
74 | if (result == 0) {
75 | File[] libs = chooser.getSelectedFiles();
76 | for (File lib : libs) {
77 | PreRuntime.libraries.addElement(lib.toString());
78 | }
79 | }
80 | }
81 | });
82 |
83 | this.add(addButton, gbc_addButton);
84 |
85 | JButton removeButton = new JButton("Remove");
86 | GridBagConstraints gbc_removeButton = new GridBagConstraints();
87 | gbc_removeButton.insets = new Insets(0, 0, 5, 5);
88 | gbc_removeButton.gridx = 3;
89 | gbc_removeButton.gridy = 1;
90 | removeButton.addActionListener((e) -> {
91 | List removeList = libList.getSelectedValuesList();
92 | if (removeList.isEmpty())
93 | return;
94 |
95 | for (String s : removeList) {
96 | PreRuntime.libraries.removeElement(s);
97 | }
98 | });
99 | this.add(removeButton, gbc_removeButton);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/manager/ClassManager.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.manager;
2 |
3 | import com.google.common.io.ByteStreams;
4 | import dev.sim0n.caesium.Caesium;
5 | import dev.sim0n.caesium.exception.CaesiumException;
6 | import dev.sim0n.caesium.mutator.impl.ClassFolderMutator;
7 | import dev.sim0n.caesium.mutator.impl.crasher.ImageCrashMutator;
8 | import dev.sim0n.caesium.util.ByteUtil;
9 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
10 | import lombok.Getter;
11 | import org.apache.logging.log4j.Logger;
12 | import org.objectweb.asm.tree.ClassNode;
13 |
14 | import java.io.*;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.Optional;
18 | import java.util.concurrent.atomic.AtomicBoolean;
19 | import java.util.stream.IntStream;
20 | import java.util.zip.ZipEntry;
21 | import java.util.zip.ZipInputStream;
22 | import java.util.zip.ZipOutputStream;
23 |
24 | @Getter
25 | public class ClassManager {
26 | private final Caesium caesium = Caesium.getInstance();
27 |
28 | private final MutatorManager mutatorManager = caesium.getMutatorManager();
29 |
30 | private final Logger logger = Caesium.getLogger();
31 |
32 | private final Map classes = new HashMap<>();
33 | private final Map resources = new HashMap<>();
34 |
35 | private final ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
36 |
37 | public void parseJar(File input) throws Exception {
38 | logger.info("Loading classes...");
39 |
40 | try (ZipInputStream zis = new ZipInputStream(new FileInputStream(input))) {
41 | ZipEntry entry;
42 |
43 | while ((entry = zis.getNextEntry()) != null) {
44 | byte[] data = ByteStreams.toByteArray(zis);
45 |
46 | String name = entry.getName();
47 |
48 | if (name.endsWith(".class")) {
49 | ClassNode classNode = ByteUtil.parseClassBytes(data);
50 |
51 | classes.put(new ClassWrapper(classNode, false), name);
52 | } else {
53 | if (name.equals("META-INF/MANIFEST.MF")) {
54 | String manifest = new String(data);
55 |
56 | // delete this line
57 | manifest = manifest.substring(0, manifest.length() - 2);
58 |
59 | // watermark the manifest
60 | manifest += String.format("Obfuscated-By: Caesium %s\r\n", Caesium.VERSION);
61 |
62 | data = manifest.getBytes();
63 | }
64 |
65 | resources.put(name, data);
66 | }
67 | }
68 | }
69 |
70 | logger.info("Loaded {} classes for mutation", classes.size());
71 | caesium.separator();
72 | }
73 |
74 | public void handleMutation() throws Exception {
75 | try (ZipOutputStream out = new ZipOutputStream(outputBuffer)) {
76 | Optional imageCrashMutator = Optional.ofNullable(mutatorManager.getMutator(ImageCrashMutator.class));
77 | Optional classFolderMutator = Optional.ofNullable(mutatorManager.getMutator(ClassFolderMutator.class));
78 |
79 | AtomicBoolean hideClasses = new AtomicBoolean(classFolderMutator.isPresent() && classFolderMutator.get().isEnabled());
80 |
81 | imageCrashMutator.ifPresent(crasher -> {
82 | if (!crasher.isEnabled())
83 | return;
84 |
85 | ClassWrapper wrapper = crasher.getCrashClass();
86 |
87 | classes.put(wrapper, String.format("%s.class", wrapper.node.name));
88 | });
89 |
90 | classes.forEach((node, name) -> {
91 | mutatorManager.handleMutation(node);
92 |
93 | try {
94 | if (hideClasses.get()) // turn them into folders
95 | name += "/";
96 |
97 | out.putNextEntry(new ZipEntry(name));
98 | out.write(ByteUtil.getClassBytes(node.node));
99 |
100 | if (hideClasses.get()) { // generate a bunch of fake classes
101 | String finalName = name;
102 |
103 | IntStream.range(0, 1 + caesium.getRandom().nextInt(10))
104 | .forEach(i -> {
105 | try {
106 | out.putNextEntry(new ZipEntry(String.format("%scaesium_%d.class", finalName, i ^ 27)));
107 | out.write(new byte[] { 0 });
108 | } catch (Exception e) {
109 | e.printStackTrace();
110 | }
111 | });
112 | }
113 | } catch (Exception e) {
114 | e.printStackTrace();
115 | }
116 | });
117 |
118 | resources.forEach((name, data) -> {
119 | try {
120 | out.putNextEntry(new ZipEntry(name));
121 | out.write(data);
122 | } catch (IOException e) {
123 | e.printStackTrace();
124 | }
125 | });
126 | }
127 |
128 | mutatorManager.handleMutationFinish();
129 | }
130 |
131 | /**
132 | * Exports {@param output}
133 | * @param output The obfuscated file to export
134 | * @throws CaesiumException If unable to write output data
135 | */
136 | public void exportJar(File output) throws CaesiumException {
137 | try {
138 | FileOutputStream fos = new FileOutputStream(output);
139 |
140 | fos.write(outputBuffer.toByteArray());
141 | fos.close();
142 | } catch (IOException e) {
143 | throw new CaesiumException("Failed to write output data", e);
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/manager/MutatorManager.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.manager;
2 |
3 | import dev.sim0n.caesium.Caesium;
4 | import dev.sim0n.caesium.mutator.ClassMutator;
5 | import dev.sim0n.caesium.mutator.impl.*;
6 | import dev.sim0n.caesium.mutator.impl.crasher.BadAnnotationMutator;
7 | import dev.sim0n.caesium.mutator.impl.crasher.ImageCrashMutator;
8 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
9 | import lombok.Getter;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | @Getter
15 | public class MutatorManager {
16 | private final Caesium caesium = Caesium.getInstance();
17 |
18 | private final List mutators = new ArrayList<>();
19 |
20 | public MutatorManager() {
21 | mutators.add(new ClassFolderMutator());
22 |
23 | mutators.add(new ImageCrashMutator());
24 |
25 | mutators.add(new ShuffleMutator());
26 | mutators.add(new TrimMutator());
27 |
28 | mutators.add(new LineNumberMutator());
29 | mutators.add(new LocalVariableMutator());
30 | mutators.add(new StringMutator());
31 | mutators.add(new ControlFlowMutator());
32 | mutators.add(new NumberMutator());
33 | mutators.add(new PolymorphMutator());
34 | mutators.add(new ReferenceMutator());
35 |
36 | // mutators.add(new AttributeMutator());
37 | mutators.add(new BadAnnotationMutator());
38 | }
39 |
40 | @SuppressWarnings("unchecked")
41 | public T getMutator(Class clazz) {
42 | return (T) mutators.stream()
43 | .filter(mutator -> mutator.getClass() == clazz)
44 | .findFirst()
45 | .orElse(null);
46 | }
47 |
48 | public void handleMutation(ClassWrapper clazz) {
49 | mutators.stream()
50 | .filter(ClassMutator::isEnabled)
51 | .forEach(mutator -> mutator.handle(clazz));
52 | }
53 |
54 | public void handleMutationFinish() {
55 | mutators.stream()
56 | .filter(ClassMutator::isEnabled)
57 | .forEach(mutator -> {
58 | mutator.handleFinish();
59 |
60 | caesium.separator();
61 | });
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/ClassMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator;
2 |
3 | import dev.sim0n.caesium.Caesium;
4 | import dev.sim0n.caesium.util.StringUtil;
5 | import dev.sim0n.caesium.util.trait.Finishable;
6 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
7 | import lombok.Getter;
8 | import lombok.Setter;
9 | import org.apache.logging.log4j.Logger;
10 | import org.objectweb.asm.Opcodes;
11 |
12 | import java.security.SecureRandom;
13 | import java.util.stream.IntStream;
14 |
15 | public abstract class ClassMutator implements Opcodes, Finishable {
16 | protected final Caesium caesium = Caesium.getInstance();
17 | protected final Logger logger = Caesium.getLogger();
18 |
19 | protected final SecureRandom random = caesium.getRandom();
20 |
21 | protected int counter;
22 |
23 | @Getter @Setter
24 | private boolean enabled = false;
25 |
26 | public abstract void handle(ClassWrapper wrapper);
27 |
28 | /**
29 | * Generates a random name using {@link SecureRandom#nextInt}
30 | * @return A random name
31 | */
32 | public String getRandomName() {
33 | switch (caesium.getDictionary()) {
34 | case ABC_LOWERCASE:
35 | return StringUtil.getRandomString(3, 6, false);
36 |
37 | case ABC:
38 | return StringUtil.getRandomString(3, 6, true);
39 |
40 | case III: {
41 | StringBuilder sb = new StringBuilder();
42 |
43 | IntStream.range(0, 20).forEach(i -> sb.append(random.nextBoolean() ? "I" : "l"));
44 |
45 | return sb.toString();
46 | }
47 |
48 | case NUMBERS:
49 | return String.valueOf(random.nextInt());
50 |
51 | case WACK: {
52 | StringBuilder sb = new StringBuilder();
53 |
54 | IntStream.range(0, 20).forEach(i -> {
55 | if (random.nextBoolean())
56 | sb.append("\n");
57 |
58 | sb.append(random.nextBoolean() ? "I" : "l");
59 | });
60 |
61 | return sb.toString();
62 | }
63 |
64 | default:
65 | return "Unsupported";
66 | }
67 |
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/AttributeMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 | import org.objectweb.asm.tree.ClassNode;
6 |
7 | public class AttributeMutator extends ClassMutator {
8 | public AttributeMutator() {
9 | setEnabled(true);
10 | }
11 |
12 | @Override
13 | public void handle(ClassWrapper wrapper) {
14 | ClassNode node = wrapper.node;
15 |
16 | }
17 |
18 | @Override
19 | public void handleFinish() {
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/ClassFolderMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 |
6 | /**
7 | * This will turn all classes into directories by append a / to .class
8 | */
9 | public class ClassFolderMutator extends ClassMutator {
10 | @Override
11 | public void handle(ClassWrapper wrapper) {
12 | ++counter;
13 | }
14 |
15 | @Override
16 | public void handleFinish() {
17 | logger.info("Turned {} classes into folders", counter);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/ControlFlowMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 | import dev.sim0n.caesium.util.wrapper.impl.MethodWrapper;
6 | import org.objectweb.asm.Label;
7 | import org.objectweb.asm.tree.*;
8 |
9 | import java.util.concurrent.ThreadLocalRandom;
10 |
11 | public class ControlFlowMutator extends ClassMutator {
12 | private String jumpIntCondField;
13 | private String jumpBoolCondField;
14 |
15 | private int jumpIntCond;
16 | private boolean jumpBoolCond;
17 |
18 | @Override
19 | public void handle(ClassWrapper wrapper) {
20 | wrapper.methods.stream()
21 | .filter(MethodWrapper::hasInstructions)
22 | .forEach(method -> {
23 | InsnList instructions = method.node.instructions;
24 |
25 | obfuscateConditions(method, instructions);
26 | addFakeJumps(wrapper, instructions);
27 | });
28 |
29 | // make the field conditions
30 | if (jumpIntCondField != null) {
31 | wrapper.addField(new FieldNode(ACC_PRIVATE | ACC_STATIC, jumpIntCondField, "I",
32 | null, jumpIntCond));
33 |
34 | jumpIntCondField = null;
35 | }
36 |
37 | if (jumpBoolCondField != null) {
38 | wrapper.addField(new FieldNode(ACC_PRIVATE | ACC_STATIC, jumpBoolCondField, "Z",
39 | null, jumpBoolCond));
40 |
41 | jumpBoolCondField = null;
42 | }
43 | }
44 |
45 | private void obfuscateConditions(MethodWrapper wrapper, InsnList instructions) {
46 | AbstractInsnNode insn = instructions.getFirst();
47 |
48 | /* do {
49 | int opcode = insn.getOpcode();
50 |
51 | if (opcode == IFEQ) {
52 | int var = ++wrapper.node.maxLocals;
53 |
54 | InsnList insns = new InsnList();
55 |
56 | insns.add(new InsnNode(ICONST_0));
57 | insns.add(new VarInsnNode(ISTORE, var));
58 |
59 | instructions.insert(insn, insns);
60 | }
61 | } while ((insn = insn.getNext()) != null);*/
62 | }
63 |
64 | /**
65 | * Replaces a jump instruction with a fake jump
66 | * @param wrapper The class wrapper
67 | * @param instructions The instructions of the method to obfuscate
68 | */
69 | private void addFakeJumps(ClassWrapper wrapper, InsnList instructions) {
70 | AbstractInsnNode insn = instructions.getFirst();
71 |
72 | do {
73 | int opcode = insn.getOpcode();
74 |
75 | if (opcode == GOTO) {
76 | // we want to switch between comparing ints and booleans
77 | boolean useInt = random.nextBoolean();
78 |
79 | if (useInt ? jumpIntCondField == null : jumpBoolCondField == null) {
80 | if (useInt) {
81 | jumpIntCondField = getRandomName();
82 |
83 | jumpIntCond = ThreadLocalRandom.current().nextInt();
84 | } else {
85 | jumpBoolCondField = getRandomName();
86 |
87 | jumpBoolCond = random.nextBoolean();
88 | }
89 | }
90 |
91 | InsnList jump = new InsnList();
92 |
93 | jump.add(new FieldInsnNode(GETSTATIC, wrapper.node.name, useInt ? jumpIntCondField : jumpBoolCondField, useInt ? "I" : "Z"));
94 |
95 | int jumpOpcode = useInt ? jumpIntCond < 0 ? IFLT : IFGE : jumpBoolCond ? IFNE : IFEQ;
96 |
97 | jump.add(new JumpInsnNode(jumpOpcode, ((JumpInsnNode) insn).label));
98 |
99 | jump.add(new InsnNode(ACONST_NULL));
100 | jump.add(new InsnNode(ATHROW));
101 |
102 | AbstractInsnNode last = jump.getLast();
103 |
104 | instructions.insert(insn, jump);
105 | instructions.remove(insn);
106 |
107 | insn = last;
108 |
109 | ++counter;
110 | }
111 | } while ((insn = insn.getNext()) != null);
112 | }
113 |
114 | @Override
115 | public void handleFinish() {
116 | logger.info("Added {} fake jumps", counter);
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/LineNumberMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 | import dev.sim0n.caesium.util.wrapper.impl.MethodWrapper;
6 | import lombok.Setter;
7 | import org.objectweb.asm.tree.InsnList;
8 | import org.objectweb.asm.tree.LineNumberNode;
9 |
10 | import java.util.stream.Stream;
11 |
12 | @Setter
13 | public class LineNumberMutator extends ClassMutator {
14 |
15 | /**
16 | * 0 = remove
17 | * 1 = scramble
18 | */
19 | private int type = 0;
20 |
21 | @Override
22 | public void handle(ClassWrapper wrapper) {
23 | switch (type) {
24 | case 0:
25 | wrapper.node.sourceFile = null;
26 | break;
27 |
28 | case 1:
29 | wrapper.node.sourceFile = getRandomName();
30 | break;
31 | }
32 |
33 | wrapper.methods.stream()
34 | .filter(MethodWrapper::hasInstructions)
35 | .forEach(method -> {
36 | InsnList instructions = method.getInstructions();
37 |
38 | Stream.of(instructions.toArray())
39 | .filter(LineNumberNode.class::isInstance)
40 | .map(LineNumberNode.class::cast)
41 | .forEach(lineNumber -> {
42 | switch (type) {
43 | case 0:
44 | instructions.remove(lineNumber);
45 | break;
46 |
47 | case 1:
48 | lineNumber.line = Math.abs(random.nextInt(2000));
49 | break;
50 | }
51 |
52 | ++counter;
53 | });
54 | });
55 | }
56 |
57 | @Override
58 | public void handleFinish() {
59 | String output = "";
60 |
61 | switch (type) {
62 | case 0:
63 | output = "Removed {} line numbers";
64 | break;
65 |
66 | case 1:
67 | output = "Scrambled {} line numbers";
68 | break;
69 | }
70 |
71 | logger.info(output, counter);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/LocalVariableMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 | import dev.sim0n.caesium.util.wrapper.impl.MethodWrapper;
6 | import lombok.Setter;
7 | import org.objectweb.asm.tree.MethodNode;
8 |
9 | public class LocalVariableMutator extends ClassMutator {
10 |
11 | @Setter
12 | private int type = 0;
13 |
14 | @Override
15 | public void handle(ClassWrapper wrapper) {
16 | wrapper.methods.stream()
17 | .filter(MethodWrapper::hasInstructions)
18 | .map(m -> m.node)
19 | .forEach(method -> {
20 | switch (type) {
21 | case 0:
22 | if (method.localVariables != null && method.localVariables.size() > 0)
23 | method.localVariables.clear();
24 |
25 | if (method.parameters != null && method.parameters.size() > 0)
26 | method.parameters.clear();
27 | break;
28 |
29 | case 1:
30 | if (method.localVariables != null && method.localVariables.size() > 0)
31 | method.localVariables.forEach(var -> var.name = getRandomName());
32 |
33 | if (method.parameters != null && method.parameters.size() > 0)
34 | method.parameters.forEach(parameter -> parameter.name = getRandomName());
35 | break;
36 | }
37 |
38 | ++counter;
39 | });
40 | }
41 |
42 | @Override
43 | public void handleFinish() {
44 | String output = "";
45 |
46 | switch (type) {
47 | case 0:
48 | output = "Removed {} local variables & parameters";
49 | break;
50 |
51 | case 1:
52 | output = "Renamed {} local variables & parameters";
53 | break;
54 | }
55 |
56 | logger.info(output, counter);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/NumberMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.ASMUtil;
5 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
6 | import dev.sim0n.caesium.util.wrapper.impl.MethodWrapper;
7 | import org.objectweb.asm.tree.*;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | public class NumberMutator extends ClassMutator {
13 | private final InsnList deobfInsns = new InsnList();
14 |
15 | private int numbers;
16 |
17 | @Override
18 | public void handle(ClassWrapper wrapper) {
19 | numbers = 0;
20 |
21 | if ((wrapper.node.access & ACC_INTERFACE) != 0)
22 | return;
23 |
24 | List insns = new ArrayList<>();
25 |
26 | wrapper.methods.stream()
27 | .filter(MethodWrapper::hasInstructions)
28 | .forEach(method -> {
29 | InsnList instructions = method.getInstructions();
30 |
31 | insns.add(instructions);
32 |
33 | AbstractInsnNode insn = instructions.getFirst();
34 |
35 | do {
36 | int opcode = insn.getOpcode();
37 |
38 | if (opcode < ICONST_M1 || opcode > LDC)
39 | continue;
40 |
41 | ++numbers;
42 | } while ((insn = insn.getNext()) != null);
43 | });
44 |
45 | if (numbers > 1000) // this is wayyy too much
46 | return;
47 |
48 | insns.forEach(instructions -> {
49 | AbstractInsnNode insn = instructions.getFirst();
50 |
51 | do {
52 | int opcode = insn.getOpcode();
53 |
54 | if (opcode < ICONST_M1 || opcode > LDC)
55 | continue;
56 |
57 | AbstractInsnNode newInsn;
58 |
59 | if (opcode <= ICONST_5) {
60 | newInsn = generateInsn(wrapper, opcode - ICONST_0);
61 | } else {
62 | switch (opcode) {
63 | case BIPUSH:
64 | case SIPUSH:
65 | newInsn = generateInsn(wrapper, ((IntInsnNode) insn).operand);
66 | break;
67 |
68 | case LDC:
69 | LdcInsnNode ldc = (LdcInsnNode) insn;
70 |
71 | if (ldc.cst instanceof Number) {
72 | newInsn = generateInsn(wrapper, (Number) ldc.cst);
73 | } else {
74 | continue;
75 | }
76 |
77 | break;
78 |
79 | default:
80 | continue;
81 | }
82 | }
83 |
84 | instructions.set(insn, newInsn);
85 |
86 | insn = newInsn;
87 |
88 | ++counter;
89 | } while ((insn = insn.getNext()) != null);
90 | });
91 |
92 | MethodNode clinit = wrapper.getClinit();
93 |
94 | clinit.instructions.insert(deobfInsns);
95 |
96 | deobfInsns.clear();
97 | }
98 |
99 | @Override
100 | public void handleFinish() {
101 | logger.info("Mutated {} numbers", counter);
102 | }
103 |
104 | /**
105 | * Generates a push instruction
106 | * @param clazz The class that owns the constants
107 | * @param value The value to generate a push instruction for
108 | * @return A push instruction
109 | */
110 | private AbstractInsnNode generateInsn(ClassWrapper clazz, Number value) {
111 | ClassNode owningClass = clazz.node;
112 |
113 | String fieldName = getRandomName();
114 | String className = value.getClass().getSimpleName();
115 |
116 | String desc = String.valueOf(className.charAt(0));
117 |
118 | switch (className) {
119 | case "Integer":
120 | deobfInsns.add(getIntPush((Integer) value));
121 | break;
122 |
123 | case "Long":
124 | // we need to change desc here because Long has a different desc
125 | desc = "J";
126 | deobfInsns.add(getLongPush((Long) value));
127 | break;
128 |
129 | case "Float":
130 | deobfInsns.add(getFloatPush((Float) value));
131 | break;
132 |
133 | case "Double":
134 | deobfInsns.add(getDoublePush((Double) value));
135 | break;
136 |
137 | default:
138 | throw new IllegalArgumentException();
139 |
140 | }
141 |
142 | deobfInsns.add(new FieldInsnNode(PUTSTATIC, owningClass.name, fieldName, desc));
143 |
144 | clazz.addField(new FieldNode(ACC_PRIVATE | ACC_STATIC, fieldName, desc, null, null));
145 |
146 | return new FieldInsnNode(GETSTATIC, owningClass.name, fieldName, desc);
147 | }
148 |
149 | // rotates left
150 | private int getRotatedInt(int value, int shiftDist) {
151 | return (value << shiftDist) | (value >>> -shiftDist);
152 | }
153 |
154 | private InsnList getIntPush(int value) {
155 | InsnList instructions = new InsnList();
156 |
157 | boolean reverse = random.nextBoolean();
158 |
159 | if (reverse) {
160 | instructions.add(ASMUtil.getOptimisedInt(Integer.reverse(value)));
161 | instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Integer", "reverse", "(I)I", false));
162 |
163 | return instructions;
164 | }
165 |
166 | int shift = random.nextInt() & 255;
167 |
168 | // this will rotate back right
169 | instructions.add(ASMUtil.getOptimisedInt(getRotatedInt(value, shift)));
170 | instructions.add(ASMUtil.getOptimisedInt(shift));
171 |
172 | instructions.add(new InsnNode(IUSHR));
173 | instructions.add(ASMUtil.getOptimisedInt(getRotatedInt(value, shift)));
174 | instructions.add(ASMUtil.getOptimisedInt(shift));
175 |
176 | if (random.nextBoolean()) {
177 | // @see https://itzsomebody.xyz/2020/03/29/math-obfuscation-of-java-bytecode.html
178 | instructions.add(new InsnNode(ICONST_M1));
179 | instructions.add(new InsnNode(IXOR));
180 | instructions.add(new InsnNode(ICONST_1));
181 | instructions.add(new InsnNode(IADD));
182 | } else {
183 | instructions.add(new InsnNode(INEG));
184 | }
185 |
186 | instructions.add(new InsnNode(ISHL));
187 | instructions.add(new InsnNode(IOR));
188 |
189 | // x & 0xFFFFFFFF will always be the same value
190 | if (random.nextBoolean()) {
191 | instructions.add(new LdcInsnNode(0xFFFFFFFF));
192 | instructions.add(new InsnNode(IAND));
193 | }
194 |
195 | return instructions;
196 | }
197 |
198 | private InsnList getLongPush(long value) {
199 | InsnList instructions = new InsnList();
200 |
201 | instructions.add(new LdcInsnNode(Long.reverse(value)));
202 | instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Long", "reverse", "(J)J", false));
203 |
204 | return instructions;
205 | }
206 |
207 | private InsnList getFloatPush(float value) {
208 | InsnList instructions = new InsnList();
209 |
210 | instructions.add(getIntPush(Float.floatToIntBits(value)));
211 | instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Float", "intBitsToFloat", "(I)F", false));
212 |
213 | return instructions;
214 | }
215 |
216 | private InsnList getDoublePush(double value) {
217 | InsnList instructions = new InsnList();
218 |
219 | instructions.add(getLongPush(Double.doubleToLongBits(value)));
220 | instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Double", "longBitsToDouble", "(J)D", false));
221 |
222 | return instructions;
223 | }
224 |
225 | }
226 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/PolymorphMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 | import dev.sim0n.caesium.util.wrapper.impl.MethodWrapper;
6 | import org.objectweb.asm.tree.InsnList;
7 | import org.objectweb.asm.tree.InsnNode;
8 | import org.objectweb.asm.tree.IntInsnNode;
9 | import org.objectweb.asm.tree.LdcInsnNode;
10 |
11 | import java.util.concurrent.ThreadLocalRandom;
12 | import java.util.concurrent.atomic.AtomicInteger;
13 | import java.util.stream.Stream;
14 |
15 | /**
16 | * This will generate useless instructions which (may) make it more annoying for the attacker to deal with
17 | */
18 | public class PolymorphMutator extends ClassMutator {
19 |
20 | @Override
21 | public void handle(ClassWrapper wrapper) {
22 | wrapper.methods.stream()
23 | .filter(MethodWrapper::hasInstructions)
24 | .forEach(method -> {
25 | InsnList instructions = method.getInstructions();
26 |
27 | AtomicInteger index = new AtomicInteger();
28 |
29 | Stream.of(instructions.toArray())
30 | .forEach(insn -> {
31 | if (insn instanceof LdcInsnNode) {
32 | if (random.nextBoolean()) {
33 | instructions.insertBefore(insn, new IntInsnNode(BIPUSH, ThreadLocalRandom.current().nextInt(-64, 64)));
34 | instructions.insertBefore(insn, new InsnNode(POP));
35 |
36 | ++counter;
37 | }
38 | } else if (index.getAndIncrement() % 6 == 0) {
39 | if (random.nextFloat() > 0.6) {
40 | instructions.insertBefore(insn, new IntInsnNode(BIPUSH, ThreadLocalRandom.current().nextInt(-27, 37)));
41 | instructions.insertBefore(insn, new InsnNode(POP));
42 | } else {
43 | instructions.insertBefore(insn, new InsnNode(NOP));
44 | }
45 |
46 | ++counter;
47 | }
48 | });
49 | });
50 | }
51 |
52 | @Override
53 | public void handleFinish() {
54 | logger.info("Inserted {} useless instructions", counter);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/ReferenceMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import com.google.common.base.Strings;
4 | import dev.sim0n.caesium.mutator.ClassMutator;
5 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
6 | import dev.sim0n.caesium.util.wrapper.impl.MethodWrapper;
7 | import org.objectweb.asm.*;
8 | import org.objectweb.asm.tree.*;
9 |
10 | import java.util.ArrayList;
11 | import java.util.Collections;
12 | import java.util.List;
13 | import java.util.concurrent.atomic.AtomicBoolean;
14 | import java.util.concurrent.atomic.AtomicReference;
15 | import java.util.stream.IntStream;
16 | import java.util.stream.Stream;
17 |
18 | /**
19 | * This hides method invocations with invokedynamics
20 | */
21 | public class ReferenceMutator extends ClassMutator {
22 | // 0 = light
23 | // 1 = normal
24 | private int type = 1;
25 |
26 | private int opcodeRandomKey;
27 | private int opcodeComparisonRandomKey;
28 |
29 | private String opcodeRandomKeyName;
30 | private String opcodeComparisonRandomKeyName;
31 |
32 | private String bsmSig;
33 | private String bsmName;
34 |
35 | private String base64RandomTable;
36 | private String decodeMethodName;
37 | private String decodeMethodSig;
38 | private int extraParamsCount;
39 |
40 | private Handle bootstrapMethodHandle = null;
41 |
42 | @Override
43 | public void handle(ClassWrapper wrapper) {
44 | if (wrapper.node.version < V1_8)
45 | return;
46 |
47 | if ((wrapper.node.access & ACC_INTERFACE) != 0)
48 | return;
49 |
50 | AtomicBoolean appliedInvoke = new AtomicBoolean(false);
51 |
52 | opcodeRandomKeyName = getRandomName();
53 | opcodeComparisonRandomKeyName = getRandomName();
54 | bsmName = getRandomName();
55 |
56 | opcodeRandomKey = random.nextInt();
57 | opcodeComparisonRandomKey = 184;
58 |
59 | base64RandomTable = getRandomTable();
60 | decodeMethodName = getRandomName();
61 | byte encryptionKey = (byte) (opcodeRandomKey & 0xFF);
62 |
63 | // To make deobfuscation more difficult we'll generate fake parameters
64 | extraParamsCount = random.nextInt(6);
65 | String extraDesc = Strings.repeat("Ljava/lang/Object;", extraParamsCount);
66 |
67 | bsmSig = String.format("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;%s)Ljava/lang/Object;", extraDesc);
68 | decodeMethodSig = String.format("(Ljava/lang/String;B%s)[B", extraDesc);
69 | String targetName = wrapper.node.name;
70 |
71 | bootstrapMethodHandle = new Handle(H_INVOKESTATIC, targetName, bsmName, bsmSig);
72 |
73 | // don't want to deal with sub classes
74 | if (targetName.contains("$"))
75 | return;
76 |
77 | wrapper.methods.stream()
78 | .filter(MethodWrapper::hasInstructions)
79 | .forEach(method -> {
80 | InsnList instructions = method.getInstructions();
81 |
82 | Stream.of(method.getInstructions().toArray())
83 | .filter(MethodInsnNode.class::isInstance)
84 | .map(MethodInsnNode.class::cast)
85 | // ignore these for now
86 | .filter(insn -> !insn.owner.startsWith("[L"))
87 | .filter(insn -> !insn.name.startsWith("<"))
88 | .forEach(insn -> {
89 | int opcode = insn.getOpcode();
90 |
91 | String newSig = opcode == INVOKESTATIC ? insn.desc : insn.desc.replace("(", "(Ljava/lang/Object;");
92 |
93 | Type origReturnType = Type.getReturnType(newSig);
94 | Type[] args = Type.getArgumentTypes(newSig);
95 |
96 | for (int i = 0; i < args.length; i++) {
97 | Type type = args[i];
98 |
99 | args[i] = type.getSort() == Type.OBJECT ? Type.getType(Object.class) : type;
100 | }
101 |
102 | newSig = Type.getMethodDescriptor(origReturnType, args);
103 |
104 | switch (opcode) {
105 | case INVOKEVIRTUAL:
106 | case INVOKESTATIC:
107 | case INVOKEINTERFACE:
108 | Object[] params = new Object[4 + extraParamsCount];
109 |
110 |
111 | params[0] = extraParamsCount != 0 ? (opcode ^ opcodeRandomKey) : opcode;
112 | params[1] = base64Encode(insn.owner.replaceAll("/", ".").getBytes(), base64RandomTable, encryptionKey);
113 | params[2] = base64Encode(insn.name.getBytes(), base64RandomTable, encryptionKey);
114 | params[3] = insn.desc;
115 |
116 | // generate random parameter objects
117 | for (int i = 0; i < extraParamsCount; i++)
118 | params[4 + i] = getRandomObject();
119 |
120 | InvokeDynamicInsnNode invokeInsn = new InvokeDynamicInsnNode(getRandomName(), newSig, bootstrapMethodHandle, params);
121 |
122 | instructions.insert(insn, invokeInsn);
123 |
124 | if (origReturnType.getSort() == Type.ARRAY)
125 | instructions.insert(invokeInsn, new TypeInsnNode(CHECKCAST, origReturnType.getInternalName()));
126 |
127 | instructions.remove(insn);
128 | appliedInvoke.set(true);
129 |
130 | ++counter;
131 | break;
132 | }
133 | });
134 | });
135 |
136 | // we didn't replace any instructions with an invoke dynamic so don't add all this stuff
137 | if (!appliedInvoke.get())
138 | return;
139 |
140 | wrapper.addField(new FieldNode(ACC_PRIVATE + ACC_STATIC, opcodeRandomKeyName, "I", null, null));
141 | wrapper.addField(new FieldNode(ACC_PRIVATE + ACC_STATIC, opcodeComparisonRandomKeyName, "I", null, null));
142 |
143 | MethodNode clinit = wrapper.getClinit();
144 |
145 | InsnList insns = new InsnList();
146 |
147 | insns.add(new LdcInsnNode(opcodeRandomKey));
148 | insns.add(new FieldInsnNode(PUTSTATIC, targetName, opcodeRandomKeyName, "I"));
149 | insns.add(new LabelNode());
150 | insns.add(new LdcInsnNode(opcodeComparisonRandomKey));
151 | insns.add(new FieldInsnNode(PUTSTATIC, targetName, opcodeComparisonRandomKeyName, "I"));
152 |
153 | clinit.instructions.insert(insns);
154 | insertDecodeMethod(wrapper.node);
155 | if (extraParamsCount == 0) { // no extra parameters so insert default bootstrap function
156 | insertMethod(wrapper.node);
157 | } else {
158 | insertExtraParamMethod(wrapper.node);
159 | }
160 | }
161 |
162 | public String base64Encode(byte[] bytes, String table, byte opcodeRandomKey) {
163 | StringBuilder result = new StringBuilder();
164 | int length = bytes.length;
165 | int mod = 0;
166 | byte prev = 0;
167 | for (int i = 0; i < length; i++) {
168 | bytes[i] ^= opcodeRandomKey;
169 | }
170 | for (int i = 0; i < length; i++) {
171 | mod = i % 3;
172 | if (mod == 0) {
173 | result.append(table.charAt((bytes[i] >> 2) & 0x3F));
174 | } else if (mod == 1) {
175 | result.append(table.charAt((prev << 4 | bytes[i] >> 4 & 0x0F) & 0x3F));
176 | } else {
177 | result.append(table.charAt((bytes[i] >> 6 & 0x03 | prev << 2) & 0x3F));
178 | result.append(table.charAt(bytes[i] & 0x3F));
179 | }
180 | prev = bytes[i];
181 | }
182 | if (mod == 0) {
183 | result.append(table.charAt(prev << 4 & 0x3C));
184 | result.append("==");
185 | } else if (mod == 1) {
186 | result.append(table.charAt(prev << 2 & 0x3F));
187 | result.append("=");
188 | }
189 | return result.toString();
190 | }
191 |
192 | public String getRandomTable() {
193 | String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/";
194 | List list = new ArrayList();
195 | for (int i = 0; i < base.length(); i++) {
196 | list.add(base.charAt(i));
197 | }
198 | Collections.shuffle(list);
199 | base = "";
200 | for (Character ch : list) {
201 | base += ch;
202 | }
203 | return base;
204 | }
205 |
206 | @Override
207 | public void handleFinish() {
208 | logger.info("Hid {} method references", counter);
209 | }
210 |
211 | /**
212 | * Generates a random object to append to our random parameters
213 | *
214 | * @return A random object
215 | */
216 | private Object getRandomObject() {
217 | switch (random.nextInt(10)) {
218 | case 0:
219 | return random.nextLong();
220 |
221 | case 1:
222 | return random.nextInt();
223 |
224 | case 2:
225 | return getRandomName();
226 |
227 | case 3:
228 | return random.nextFloat();
229 |
230 | case 4:
231 | return random.nextGaussian();
232 |
233 | case 5:
234 | return Float.floatToIntBits(random.nextFloat());
235 |
236 | case 6: {
237 | AtomicReference s = new AtomicReference<>("");
238 |
239 | IntStream.range(0, 5 + random.nextInt(20))
240 | .forEach(i -> s.updateAndGet(v -> v + (char) random.nextInt(60)));
241 |
242 | return s.get();
243 | }
244 |
245 | case 7: {
246 | return (byte) random.nextInt(Byte.MAX_VALUE);
247 | }
248 |
249 | case 8:
250 | return random.nextDouble() * 20F;
251 |
252 | case 9:
253 | return base64Encode(getRandomName().getBytes(), base64RandomTable, (byte) 0);
254 |
255 | case 10:
256 | return -Math.abs(random.nextInt());
257 | }
258 |
259 | return 0;
260 | }
261 |
262 | public void insertDecodeMethod(ClassNode target) {
263 | MethodVisitor mv = target.visitMethod(ACC_PUBLIC | ACC_STATIC, decodeMethodName, decodeMethodSig, null, null);
264 | mv.visitCode();
265 | Label label0 = new Label();
266 | mv.visitLabel(label0);
267 | mv.visitLineNumber(12, label0);
268 | mv.visitInsn(ICONST_0);
269 | mv.visitVarInsn(ISTORE, 2);
270 | Label label1 = new Label();
271 | mv.visitLabel(label1);
272 | mv.visitLineNumber(13, label1);
273 | mv.visitLdcInsn("");
274 | mv.visitVarInsn(ASTORE, 3);
275 | Label label2 = new Label();
276 | mv.visitLabel(label2);
277 | mv.visitLineNumber(14, label2);
278 | mv.visitInsn(ICONST_0);
279 | mv.visitVarInsn(ISTORE, 4);
280 | Label label3 = new Label();
281 | mv.visitLabel(label3);
282 | mv.visitFrame(Opcodes.F_APPEND, 3, new Object[]{Opcodes.INTEGER, "java/lang/String", Opcodes.INTEGER}, 0, null);
283 | mv.visitVarInsn(ILOAD, 4);
284 | mv.visitVarInsn(ALOAD, 0);
285 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
286 | Label label4 = new Label();
287 | mv.visitJumpInsn(IF_ICMPGE, label4);
288 | Label label5 = new Label();
289 | mv.visitLabel(label5);
290 | mv.visitLineNumber(15, label5);
291 | mv.visitLdcInsn(base64RandomTable);
292 | mv.visitVarInsn(ALOAD, 0);
293 | mv.visitVarInsn(ILOAD, 4);
294 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false);
295 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "indexOf", "(I)I", false);
296 | mv.visitInsn(I2B);
297 | mv.visitVarInsn(ISTORE, 2);
298 | Label label6 = new Label();
299 | mv.visitLabel(label6);
300 | mv.visitLineNumber(16, label6);
301 | mv.visitVarInsn(ILOAD, 2);
302 | mv.visitInsn(ICONST_M1);
303 | Label label7 = new Label();
304 | mv.visitJumpInsn(IF_ICMPNE, label7);
305 | Label label8 = new Label();
306 | mv.visitLabel(label8);
307 | mv.visitLineNumber(17, label8);
308 | mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
309 | mv.visitInsn(DUP);
310 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false);
311 | mv.visitVarInsn(ALOAD, 3);
312 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
313 | mv.visitLdcInsn("000000");
314 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
315 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
316 | mv.visitVarInsn(ASTORE, 3);
317 | Label label9 = new Label();
318 | mv.visitJumpInsn(GOTO, label9);
319 | mv.visitLabel(label7);
320 | mv.visitLineNumber(19, label7);
321 | mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
322 | mv.visitVarInsn(ILOAD, 2);
323 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "toBinaryString", "(I)Ljava/lang/String;", false);
324 | mv.visitVarInsn(ASTORE, 5);
325 | Label label10 = new Label();
326 | mv.visitLabel(label10);
327 | mv.visitLineNumber(20, label10);
328 | mv.visitVarInsn(ALOAD, 5);
329 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
330 | mv.visitIntInsn(BIPUSH, 7);
331 | Label label11 = new Label();
332 | mv.visitJumpInsn(IF_ICMPNE, label11);
333 | Label label12 = new Label();
334 | mv.visitLabel(label12);
335 | mv.visitLineNumber(21, label12);
336 | mv.visitVarInsn(ALOAD, 5);
337 | mv.visitInsn(ICONST_1);
338 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(I)Ljava/lang/String;", false);
339 | mv.visitVarInsn(ASTORE, 5);
340 | Label label13 = new Label();
341 | mv.visitJumpInsn(GOTO, label13);
342 | mv.visitLabel(label11);
343 | mv.visitLineNumber(22, label11);
344 | mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"java/lang/String"}, 0, null);
345 | mv.visitVarInsn(ALOAD, 5);
346 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
347 | mv.visitIntInsn(BIPUSH, 8);
348 | mv.visitJumpInsn(IF_ICMPNE, label13);
349 | Label label14 = new Label();
350 | mv.visitLabel(label14);
351 | mv.visitLineNumber(23, label14);
352 | mv.visitVarInsn(ALOAD, 5);
353 | mv.visitInsn(ICONST_2);
354 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(I)Ljava/lang/String;", false);
355 | mv.visitVarInsn(ASTORE, 5);
356 | mv.visitLabel(label13);
357 | mv.visitLineNumber(25, label13);
358 | mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
359 | mv.visitVarInsn(ALOAD, 5);
360 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
361 | mv.visitIntInsn(BIPUSH, 6);
362 | Label label15 = new Label();
363 | mv.visitJumpInsn(IF_ICMPGE, label15);
364 | Label label16 = new Label();
365 | mv.visitLabel(label16);
366 | mv.visitLineNumber(26, label16);
367 | mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
368 | mv.visitInsn(DUP);
369 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false);
370 | mv.visitLdcInsn("0");
371 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
372 | mv.visitVarInsn(ALOAD, 5);
373 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
374 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
375 | mv.visitVarInsn(ASTORE, 5);
376 | mv.visitJumpInsn(GOTO, label13);
377 | mv.visitLabel(label15);
378 | mv.visitLineNumber(28, label15);
379 | mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
380 | mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
381 | mv.visitInsn(DUP);
382 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false);
383 | mv.visitVarInsn(ALOAD, 3);
384 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
385 | mv.visitVarInsn(ALOAD, 5);
386 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
387 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
388 | mv.visitVarInsn(ASTORE, 3);
389 | mv.visitLabel(label9);
390 | mv.visitLineNumber(14, label9);
391 | mv.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
392 | mv.visitIincInsn(4, 1);
393 | mv.visitJumpInsn(GOTO, label3);
394 | mv.visitLabel(label4);
395 | mv.visitLineNumber(31, label4);
396 | mv.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
397 | mv.visitVarInsn(ALOAD, 3);
398 | mv.visitLdcInsn("00000000");
399 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "endsWith", "(Ljava/lang/String;)Z", false);
400 | Label label17 = new Label();
401 | mv.visitJumpInsn(IFEQ, label17);
402 | Label label18 = new Label();
403 | mv.visitLabel(label18);
404 | mv.visitLineNumber(32, label18);
405 | mv.visitVarInsn(ALOAD, 3);
406 | mv.visitInsn(ICONST_0);
407 | mv.visitVarInsn(ALOAD, 3);
408 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
409 | mv.visitIntInsn(BIPUSH, 8);
410 | mv.visitInsn(ISUB);
411 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(II)Ljava/lang/String;", false);
412 | mv.visitVarInsn(ASTORE, 3);
413 | mv.visitJumpInsn(GOTO, label4);
414 | mv.visitLabel(label17);
415 | mv.visitLineNumber(34, label17);
416 | mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
417 | mv.visitVarInsn(ALOAD, 3);
418 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
419 | mv.visitIntInsn(BIPUSH, 8);
420 | mv.visitInsn(IDIV);
421 | mv.visitIntInsn(NEWARRAY, T_BYTE);
422 | mv.visitVarInsn(ASTORE, 4);
423 | Label label19 = new Label();
424 | mv.visitLabel(label19);
425 | mv.visitLineNumber(35, label19);
426 | mv.visitInsn(ICONST_0);
427 | mv.visitVarInsn(ISTORE, 5);
428 | Label label20 = new Label();
429 | mv.visitLabel(label20);
430 | mv.visitFrame(Opcodes.F_APPEND, 2, new Object[]{"[B", Opcodes.INTEGER}, 0, null);
431 | mv.visitVarInsn(ILOAD, 5);
432 | mv.visitVarInsn(ALOAD, 4);
433 | mv.visitInsn(ARRAYLENGTH);
434 | Label label21 = new Label();
435 | mv.visitJumpInsn(IF_ICMPGE, label21);
436 | Label label22 = new Label();
437 | mv.visitLabel(label22);
438 | mv.visitLineNumber(36, label22);
439 | mv.visitVarInsn(ALOAD, 4);
440 | mv.visitVarInsn(ILOAD, 5);
441 | mv.visitVarInsn(ALOAD, 3);
442 | mv.visitVarInsn(ILOAD, 5);
443 | mv.visitIntInsn(BIPUSH, 8);
444 | mv.visitInsn(IMUL);
445 | mv.visitVarInsn(ILOAD, 5);
446 | mv.visitInsn(ICONST_1);
447 | mv.visitInsn(IADD);
448 | mv.visitIntInsn(BIPUSH, 8);
449 | mv.visitInsn(IMUL);
450 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(II)Ljava/lang/String;", false);
451 | mv.visitInsn(ICONST_2);
452 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(Ljava/lang/String;I)Ljava/lang/Integer;", false);
453 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "byteValue", "()B", false);
454 | mv.visitInsn(BASTORE);
455 | Label label23 = new Label();
456 | mv.visitLabel(label23);
457 | mv.visitLineNumber(37, label23);
458 | mv.visitVarInsn(ALOAD, 4);
459 | mv.visitVarInsn(ILOAD, 5);
460 | mv.visitInsn(DUP2);
461 | mv.visitInsn(BALOAD);
462 | mv.visitVarInsn(ILOAD, 1);
463 | mv.visitInsn(IXOR);
464 | mv.visitInsn(I2B);
465 | mv.visitInsn(BASTORE);
466 | Label label24 = new Label();
467 | mv.visitLabel(label24);
468 | mv.visitLineNumber(35, label24);
469 | mv.visitIincInsn(5, 1);
470 | mv.visitJumpInsn(GOTO, label20);
471 | mv.visitLabel(label21);
472 | mv.visitLineNumber(39, label21);
473 | mv.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
474 | mv.visitVarInsn(ALOAD, 4);
475 | mv.visitInsn(ARETURN);
476 | Label label25 = new Label();
477 | mv.visitLabel(label25);
478 | mv.visitMaxs(6, 6);
479 | mv.visitEnd();
480 | }
481 |
482 | public void insertExtraParamMethod(ClassNode target) {
483 | MethodVisitor mv = target.visitMethod(ACC_PUBLIC + ACC_STATIC, bsmName, bsmSig, null, new String[]{"java/lang/Exception"});
484 | mv.visitCode();
485 | Label l0 = new Label();
486 | mv.visitLabel(l0);
487 | mv.visitLineNumber(298, l0);
488 | mv.visitVarInsn(ALOAD, 3);
489 | mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
490 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
491 | mv.visitVarInsn(ISTORE, 8);
492 | Label l1 = new Label();
493 | mv.visitLabel(l1);
494 | mv.visitLineNumber(300, l1);
495 | mv.visitVarInsn(ILOAD, 8);
496 | mv.visitFieldInsn(GETSTATIC, target.name, opcodeRandomKeyName, "I");
497 | mv.visitInsn(IXOR);
498 | mv.visitIntInsn(SIPUSH, 255);
499 | mv.visitInsn(IAND);
500 | mv.visitVarInsn(ISTORE, 8);
501 | Label l2 = new Label();
502 | mv.visitLabel(l2);
503 | mv.visitLineNumber(302, l2);
504 | mv.visitFieldInsn(GETSTATIC, target.name, opcodeComparisonRandomKeyName, "I");
505 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
506 | mv.visitVarInsn(ASTORE, 7);
507 | Label l3 = new Label();
508 | mv.visitLabel(l3);
509 | mv.visitLineNumber(304, l3);
510 |
511 | mv.visitFieldInsn(GETSTATIC, target.name, opcodeRandomKeyName, "I");
512 | mv.visitIntInsn(SIPUSH, 255);
513 | mv.visitInsn(IAND);
514 | mv.visitInsn(I2B);
515 | mv.visitVarInsn(ISTORE, 11);
516 |
517 | mv.visitTypeInsn(NEW, "java/lang/String");
518 | mv.visitInsn(DUP);
519 |
520 | mv.visitVarInsn(ALOAD, 4);
521 | mv.visitTypeInsn(CHECKCAST, "java/lang/String");
522 | mv.visitVarInsn(ILOAD, 11);
523 | for (int i = 0; i < extraParamsCount; i++) {
524 | int varOffest = i + 6;
525 | if (varOffest == 8) {
526 | mv.visitVarInsn(ILOAD, varOffest);
527 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
528 | } else mv.visitVarInsn(ALOAD, varOffest);
529 | }
530 | mv.visitMethodInsn(INVOKESTATIC, target.name, decodeMethodName, decodeMethodSig, false);
531 |
532 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([B)V", false);
533 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
534 | mv.visitVarInsn(ASTORE, 9);
535 | Label l4 = new Label();
536 | mv.visitLabel(l4);
537 | mv.visitLineNumber(305, l4);
538 | mv.visitVarInsn(ALOAD, 6);
539 | mv.visitTypeInsn(CHECKCAST, "java/lang/String");
540 | mv.visitVarInsn(ALOAD, 9);
541 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false);
542 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", false);
543 | mv.visitVarInsn(ASTORE, 10);
544 | Label l5 = new Label();
545 | mv.visitLabel(l5);
546 | mv.visitLineNumber(307, l5);
547 | mv.visitVarInsn(ILOAD, 8);
548 | mv.visitVarInsn(ALOAD, 7);
549 | mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
550 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
551 | Label l6 = new Label();
552 | mv.visitJumpInsn(IF_ICMPNE, l6);
553 | Label l7 = new Label();
554 | mv.visitLabel(l7);
555 | mv.visitLineNumber(308, l7);
556 | mv.visitTypeInsn(NEW, "java/lang/invoke/MutableCallSite");
557 | mv.visitInsn(DUP);
558 | mv.visitVarInsn(ALOAD, 0);
559 | mv.visitVarInsn(ALOAD, 9);
560 | mv.visitTypeInsn(NEW, "java/lang/String");
561 | mv.visitInsn(DUP);
562 |
563 | mv.visitVarInsn(ALOAD, 5);
564 | mv.visitTypeInsn(CHECKCAST, "java/lang/String");
565 | mv.visitVarInsn(ILOAD, 11);
566 | for (int i = 0; i < extraParamsCount; i++) {
567 | int varOffest = i + 6;
568 | if (varOffest == 8) {
569 | mv.visitVarInsn(ILOAD, varOffest);
570 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
571 | } else mv.visitVarInsn(ALOAD, varOffest);
572 | }
573 | mv.visitMethodInsn(INVOKESTATIC, target.name, decodeMethodName, decodeMethodSig, false);
574 |
575 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([B)V", false);
576 | mv.visitVarInsn(ALOAD, 10);
577 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
578 | mv.visitVarInsn(ALOAD, 2);
579 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
580 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/MutableCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false);
581 | mv.visitInsn(ARETURN);
582 | mv.visitLabel(l6);
583 | mv.visitLineNumber(310, l6);
584 | mv.visitFrame(F_APPEND, 3, new Object[]{INTEGER, "java/lang/Class", "java/lang/invoke/MethodType"}, 0, null);
585 | mv.visitTypeInsn(NEW, "java/lang/invoke/MutableCallSite");
586 | mv.visitInsn(DUP);
587 | mv.visitVarInsn(ALOAD, 0);
588 | mv.visitVarInsn(ALOAD, 9);
589 | mv.visitTypeInsn(NEW, "java/lang/String");
590 | mv.visitInsn(DUP);
591 |
592 | mv.visitVarInsn(ALOAD, 5);
593 | mv.visitTypeInsn(CHECKCAST, "java/lang/String");
594 | mv.visitVarInsn(ILOAD, 11);
595 | for (int i = 0; i < extraParamsCount; i++) {
596 | int varOffest = i + 6;
597 | if (varOffest == 8) {
598 | mv.visitVarInsn(ILOAD, varOffest);
599 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
600 | } else mv.visitVarInsn(ALOAD, varOffest);
601 | }
602 | mv.visitMethodInsn(INVOKESTATIC, target.name, decodeMethodName, decodeMethodSig, false);
603 |
604 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([B)V", false);
605 | mv.visitVarInsn(ALOAD, 10);
606 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findVirtual", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
607 | mv.visitVarInsn(ALOAD, 2);
608 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
609 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/MutableCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false);
610 | mv.visitInsn(ARETURN);
611 | Label l8 = new Label();
612 | mv.visitLabel(l8);
613 | mv.visitMaxs(7, 12);
614 | mv.visitEnd();
615 | }
616 |
617 | public void insertMethod(ClassNode target) {
618 | final MethodVisitor mv = target.visitMethod(9, bsmName, bsmSig, null, new String[]{"java/lang/Exception"});
619 | mv.visitCode();
620 | final Label l0 = new Label();
621 | mv.visitLabel(l0);
622 | mv.visitLineNumber(35, l0);
623 |
624 | mv.visitFieldInsn(GETSTATIC, target.name, opcodeRandomKeyName, "I");
625 | mv.visitIntInsn(SIPUSH, 255);
626 | mv.visitInsn(IAND);
627 | mv.visitInsn(I2B);
628 | mv.visitVarInsn(ISTORE, 10);
629 |
630 | mv.visitTypeInsn(NEW, "java/lang/String");
631 | mv.visitInsn(DUP);
632 |
633 | mv.visitVarInsn(ALOAD, 4);
634 | mv.visitTypeInsn(CHECKCAST, "java/lang/String");
635 | mv.visitVarInsn(ILOAD, 10);
636 | mv.visitMethodInsn(INVOKESTATIC, target.name, decodeMethodName, decodeMethodSig, false);
637 |
638 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([B)V", false);
639 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
640 | mv.visitVarInsn(ASTORE, 7);
641 | final Label l2 = new Label();
642 | mv.visitLabel(l2);
643 | mv.visitLineNumber(36, l2);
644 | mv.visitVarInsn(ALOAD, 7);
645 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false);
646 | mv.visitVarInsn(ASTORE, 8);
647 | final Label l3 = new Label();
648 | mv.visitLabel(l3);
649 | mv.visitLineNumber(37, l3);
650 | mv.visitVarInsn(ALOAD, 6);
651 | mv.visitTypeInsn(CHECKCAST, "java/lang/String");
652 | mv.visitVarInsn(ALOAD, 8);
653 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", false);
654 | mv.visitVarInsn(ASTORE, 9);
655 | final Label l4 = new Label();
656 | mv.visitLabel(l4);
657 | mv.visitLineNumber(39, l4);
658 | mv.visitVarInsn(ALOAD, 3);
659 | mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
660 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
661 | mv.visitIntInsn(SIPUSH, 184);
662 | final Label l5 = new Label();
663 | mv.visitJumpInsn(IF_ICMPNE, l5);
664 | final Label l6 = new Label();
665 | mv.visitLabel(l6);
666 | mv.visitLineNumber(40, l6);
667 | mv.visitTypeInsn(NEW, "java/lang/invoke/MutableCallSite");
668 | mv.visitInsn(DUP);
669 | mv.visitVarInsn(ALOAD, 0);
670 | mv.visitVarInsn(ALOAD, 7);
671 | mv.visitTypeInsn(NEW, "java/lang/String");
672 | mv.visitInsn(DUP);
673 |
674 | mv.visitVarInsn(ALOAD, 5);
675 | mv.visitTypeInsn(CHECKCAST, "java/lang/String");
676 | mv.visitVarInsn(ILOAD, 10);
677 | mv.visitMethodInsn(INVOKESTATIC, target.name, decodeMethodName, decodeMethodSig, false);
678 |
679 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([B)V", false);
680 | mv.visitVarInsn(ALOAD, 9);
681 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
682 | mv.visitVarInsn(ALOAD, 2);
683 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
684 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/MutableCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false);
685 | mv.visitInsn(ARETURN);
686 | mv.visitLabel(l5);
687 | mv.visitLineNumber(42, l5);
688 | mv.visitFrame(1, 3, new Object[]{"java/lang/Class", "java/lang/ClassLoader", "java/lang/invoke/MethodType"}, 0, null);
689 | mv.visitTypeInsn(NEW, "java/lang/invoke/MutableCallSite");
690 | mv.visitInsn(DUP);
691 | mv.visitVarInsn(ALOAD, 0);
692 | mv.visitVarInsn(ALOAD, 7);
693 | mv.visitTypeInsn(NEW, "java/lang/String");
694 | mv.visitInsn(DUP);
695 |
696 | mv.visitVarInsn(ALOAD, 5);
697 | mv.visitTypeInsn(CHECKCAST, "java/lang/String");
698 | mv.visitVarInsn(ILOAD, 10);
699 | mv.visitMethodInsn(INVOKESTATIC, target.name, decodeMethodName, decodeMethodSig, false);
700 |
701 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([B)V", false);
702 | mv.visitVarInsn(ALOAD, 9);
703 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findVirtual", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
704 | mv.visitVarInsn(ALOAD, 2);
705 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
706 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/MutableCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false);
707 | mv.visitInsn(ARETURN);
708 | final Label l7 = new Label();
709 | mv.visitLabel(l7);
710 | mv.visitMaxs(7, 11);
711 | mv.visitEnd();
712 | }
713 |
714 | }
715 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/ShuffleMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 |
6 | import java.util.Collections;
7 |
8 | // This will shuffle class members, however this can break reflection
9 | public class ShuffleMutator extends ClassMutator {
10 | @Override
11 | public void handle(ClassWrapper wrapper) {
12 | Collections.shuffle(wrapper.node.fields, random);
13 | Collections.shuffle(wrapper.node.methods, random);
14 |
15 | counter += wrapper.fields.size() + wrapper.node.methods.size();
16 | }
17 |
18 | @Override
19 | public void handleFinish() {
20 | logger.info("Shuffled {} members", counter);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/StringMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import com.google.common.base.Charsets;
4 | import dev.sim0n.caesium.mutator.ClassMutator;
5 | import dev.sim0n.caesium.util.ASMUtil;
6 | import dev.sim0n.caesium.util.StringUtil;
7 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
8 | import lombok.Getter;
9 | import org.objectweb.asm.*;
10 | import org.objectweb.asm.tree.*;
11 |
12 | import javax.crypto.Cipher;
13 | import javax.crypto.SecretKeyFactory;
14 | import javax.crypto.spec.DESKeySpec;
15 | import javax.crypto.spec.IvParameterSpec;
16 | import java.nio.charset.Charset;
17 | import java.util.*;
18 | import java.util.concurrent.ThreadLocalRandom;
19 |
20 | public class StringMutator extends ClassMutator {
21 | @Getter
22 | private final Set exclusions = new HashSet<>();
23 |
24 | private int key1;
25 |
26 | private long key2;
27 | private long key3;
28 |
29 | private String stringField1 = getRandomName();
30 | private String stringField2 = getRandomName();
31 | private String keyField = getRandomName();
32 | private String decryptMethodName = getRandomName();
33 |
34 | private String initName;
35 |
36 | private String bsmName = getRandomName();
37 | private String bsmSig = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object;";
38 | private Handle bsmHandle = null;
39 |
40 | private final List strings = new ArrayList<>();
41 |
42 | @Override
43 | public void handle(ClassWrapper wrapper) {
44 | if ((wrapper.node.access & ACC_INTERFACE) != 0)
45 | return;
46 |
47 | ClassNode target = wrapper.node;
48 |
49 | getStrings(target);
50 |
51 | if (stringCount == 0)
52 | return;
53 |
54 | key1 = ThreadLocalRandom.current().nextInt(1, 125);
55 |
56 | key2 = random.nextLong();
57 | key3 = random.nextLong();
58 |
59 | stringField1 = getRandomName();
60 | stringField2 = getRandomName();
61 |
62 | keyField = getRandomName();
63 |
64 | decryptMethodName = getRandomName();
65 | initName = getRandomName();
66 |
67 | bsmName = getRandomName();
68 | bsmHandle = new Handle(H_INVOKESTATIC, target.name, bsmName, bsmSig);
69 |
70 | {
71 | FieldVisitor fv = target.visitField(ACC_PRIVATE | ACC_STATIC, stringField1, "[Ljava/lang/String;", null, null);
72 | fv.visitEnd();
73 | }
74 |
75 | {
76 | FieldVisitor fv = target.visitField(ACC_PRIVATE | ACC_STATIC, stringField2, "[Ljava/lang/String;", null, null);
77 | fv.visitEnd();
78 | }
79 |
80 | {
81 | FieldVisitor fv = target.visitField(ACC_PRIVATE | ACC_STATIC, keyField, "J", null, null);
82 | fv.visitEnd();
83 | }
84 |
85 | MethodNode clinit = wrapper.getClinit();
86 |
87 | obfuscateStrings(target);
88 |
89 | makeCallSite(target);
90 |
91 | clinit.instructions.insert(getClinitInstructions(target));
92 |
93 | target.methods.add(makeDecryptMethod(target));
94 |
95 | target.methods.add(makeInit(target));
96 | }
97 |
98 | private int stringCount;
99 |
100 | public void getStrings(ClassNode owner) {
101 | stringCount = 0;
102 |
103 | owner.methods.stream()
104 | .filter(methodNode -> methodNode.instructions.size() > 0)
105 | .forEach(methodNode -> {
106 | InsnList instructions = methodNode.instructions;
107 | AbstractInsnNode insn = instructions.getFirst();
108 |
109 | do {
110 | if (insn.getOpcode() == LDC && ((LdcInsnNode) insn).cst instanceof String) {
111 | ++stringCount;
112 | }
113 | } while ((insn = insn.getNext()) != null);
114 | });
115 | }
116 |
117 | int index;
118 |
119 | public void obfuscateStrings(ClassNode owner) {
120 | // let's make sure to clear every string before
121 | strings.clear();
122 |
123 | owner.methods.stream()
124 | .filter(methodNode -> methodNode.instructions.size() > 0)
125 | .forEach(methodNode -> {
126 | InsnList instructions = methodNode.instructions;
127 | AbstractInsnNode insn = instructions.getFirst();
128 |
129 | do {
130 | if (insn.getOpcode() == LDC) {
131 | LdcInsnNode ldc = (LdcInsnNode) insn;
132 |
133 | if (ldc.cst instanceof String) {
134 | String string = (String) ldc.cst;
135 |
136 | if (exclusions.contains(string))
137 | continue;
138 |
139 | AbstractInsnNode ain = ASMUtil.getOptimisedInt(index);
140 |
141 | InsnList newInstructions = new InsnList();
142 |
143 | if (random.nextBoolean()) {
144 | newInstructions.add(new LdcInsnNode(key3));
145 | newInstructions.add(new LdcInsnNode(new Long(key1)));
146 | newInstructions.add(new InsnNode(LXOR));
147 | } else {
148 | // x & 0xFFFFFFFF will always be the same value
149 | if (random.nextBoolean()) {
150 | newInstructions.add(new LdcInsnNode(0xFFFFFFFF));
151 | newInstructions.add(new InsnNode(IAND));
152 | }
153 |
154 | newInstructions.add(new LdcInsnNode(key3 ^ key1));
155 | }
156 |
157 | // We want to use an invokedynamic that references the decrypt function
158 | newInstructions.add(new InvokeDynamicInsnNode(getRandomName(), "(IJ)Ljava/lang/String;", bsmHandle));
159 |
160 | instructions.set(insn, insn = ain);
161 | instructions.insert(insn, newInstructions);
162 |
163 | strings.add(string);
164 | ++index;
165 | ++counter;
166 | }
167 | }
168 | } while ((insn = insn.getNext()) != null);
169 | });
170 |
171 | // reset for next run
172 | index = 0;
173 | }
174 |
175 | @Override
176 | public void handleFinish() {
177 | logger.info("encrypted {} string literals", counter);
178 | }
179 |
180 |
181 | /**
182 | * Encrypts a string using DES
183 | * @param s The string to encrypt
184 | * @return The encrypted string
185 | */
186 | public String encryptString(String s) {
187 | try {
188 | long newKey = new Long(key3) ^ key2;
189 |
190 | Cipher instance = Cipher.getInstance("DES/CBC/PKCS5Padding");
191 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
192 |
193 | byte[] keys = new byte[8];
194 |
195 | keys[0] = (byte) (newKey >>> 56);
196 |
197 | for (int i = 1; i < 8; ++i) {
198 | keys[i] = (byte) (newKey << i * 8 >>> 56);
199 | }
200 |
201 | instance.init(Cipher.ENCRYPT_MODE, keyFactory.generateSecret(new DESKeySpec(keys)), new IvParameterSpec(new byte[8]));
202 |
203 | return new String(Base64.getEncoder().encode(instance.doFinal(s.getBytes(Charsets.UTF_8))));
204 | } catch (Exception e) {
205 | e.printStackTrace();
206 | }
207 |
208 | return null;
209 | }
210 |
211 | private void makeCallSite(ClassNode cw) {
212 | MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC, bsmName, "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object;", null, null);
213 | mv.visitCode();
214 | Label l0 = new Label();
215 | Label l1 = new Label();
216 | Label l2 = new Label();
217 |
218 | String typeName = "L" + cw.name.replace(".", "/") + ";";
219 |
220 | mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
221 | mv.visitLabel(l0);
222 | mv.visitLineNumber(73, l0);
223 | mv.visitTypeInsn(NEW, "java/lang/invoke/MutableCallSite");
224 | mv.visitInsn(DUP);
225 | mv.visitVarInsn(ALOAD, 0);
226 | mv.visitLdcInsn(Type.getType(typeName));
227 | mv.visitLdcInsn(decryptMethodName);
228 | mv.visitLdcInsn("(IJ)Ljava/lang/String;");
229 | mv.visitLdcInsn(Type.getType(typeName));
230 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false);
231 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", false);
232 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
233 | mv.visitVarInsn(ALOAD, 2);
234 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
235 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/MutableCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false);
236 | mv.visitLabel(l1);
237 | mv.visitInsn(ARETURN);
238 | mv.visitLabel(l2);
239 | mv.visitLineNumber(74, l2);
240 | mv.visitFrame(F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"});
241 | mv.visitVarInsn(ASTORE, 3);
242 | Label l3 = new Label();
243 | mv.visitLabel(l3);
244 | mv.visitLineNumber(75, l3);
245 | mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
246 | mv.visitInsn(DUP);
247 | mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
248 | mv.visitInsn(DUP);
249 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false);
250 | mv.visitLdcInsn(cw.name + ":");
251 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
252 | mv.visitVarInsn(ALOAD, 1);
253 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
254 | mv.visitLdcInsn(":");
255 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
256 | mv.visitVarInsn(ALOAD, 2);
257 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodType", "toString", "()Ljava/lang/String;", false);
258 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
259 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
260 | mv.visitVarInsn(ALOAD, 3);
261 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "", "(Ljava/lang/String;Ljava/lang/Throwable;)V", false);
262 | mv.visitInsn(ATHROW);
263 | Label l4 = new Label();
264 | mv.visitLabel(l4);
265 | mv.visitMaxs(7, 4);
266 | mv.visitEnd();
267 | }
268 |
269 | /**
270 | * Generates a decrypt method
271 | * @param owner The class owner
272 | * @return A string decryption method
273 | */
274 | public MethodNode makeDecryptMethod(ClassNode owner) {
275 | MethodNode mv = new MethodNode(ACC_PRIVATE + ACC_STATIC, decryptMethodName, "(IJ)Ljava/lang/String;", null, null);
276 | mv.visitCode();
277 | Label l0 = new Label();
278 | Label l1 = new Label();
279 | Label l2 = new Label();
280 | mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
281 | Label l3 = new Label();
282 | mv.visitLabel(l3);
283 | mv.visitLineNumber(57, l3);
284 | mv.visitVarInsn(LLOAD, 1);
285 | mv.visitLdcInsn(new Long(key1));
286 | mv.visitInsn(LXOR);
287 | mv.visitVarInsn(LSTORE, 1);
288 | Label l4 = new Label();
289 | mv.visitLabel(l4);
290 | mv.visitLineNumber(58, l4);
291 | mv.visitVarInsn(LLOAD, 1);
292 | mv.visitLdcInsn(new Long(key2));
293 | mv.visitInsn(LXOR);
294 | mv.visitVarInsn(LSTORE, 1);
295 | Label l5 = new Label();
296 | mv.visitLabel(l5);
297 | mv.visitLineNumber(60, l5);
298 | mv.visitFieldInsn(GETSTATIC, owner.name, stringField1, "[Ljava/lang/String;");
299 | mv.visitVarInsn(ILOAD, 0);
300 | mv.visitInsn(AALOAD);
301 | Label l6 = new Label();
302 | mv.visitJumpInsn(IFNONNULL, l6);
303 | mv.visitLabel(l0);
304 | mv.visitLineNumber(65, l0);
305 | mv.visitLdcInsn("DES/CBC/PKCS5Padding");
306 | mv.visitMethodInsn(INVOKESTATIC, "javax/crypto/Cipher", "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;", false);
307 | mv.visitVarInsn(ASTORE, 3);
308 | Label l7 = new Label();
309 | mv.visitLabel(l7);
310 | mv.visitLineNumber(66, l7);
311 | mv.visitLdcInsn("DES");
312 | mv.visitMethodInsn(INVOKESTATIC, "javax/crypto/SecretKeyFactory", "getInstance", "(Ljava/lang/String;)Ljavax/crypto/SecretKeyFactory;", false);
313 | mv.visitVarInsn(ASTORE, 4);
314 | mv.visitLabel(l1);
315 | mv.visitLineNumber(69, l1);
316 | Label l8 = new Label();
317 | mv.visitJumpInsn(GOTO, l8);
318 | mv.visitLabel(l2);
319 | mv.visitLineNumber(67, l2);
320 | mv.visitFrame(F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"});
321 | mv.visitVarInsn(ASTORE, 5);
322 | Label l9 = new Label();
323 | mv.visitLabel(l9);
324 | mv.visitLineNumber(68, l9);
325 | mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
326 | mv.visitInsn(DUP);
327 | mv.visitLdcInsn(owner.name);
328 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "", "(Ljava/lang/String;)V", false);
329 | mv.visitInsn(ATHROW);
330 | mv.visitLabel(l8);
331 | mv.visitLineNumber(71, l8);
332 | mv.visitFrame(F_APPEND, 2, new Object[]{"javax/crypto/Cipher", "javax/crypto/SecretKeyFactory"}, 0, null);
333 | mv.visitIntInsn(BIPUSH, 8);
334 | mv.visitIntInsn(NEWARRAY, T_BYTE);
335 | mv.visitVarInsn(ASTORE, 5);
336 | Label l10 = new Label();
337 | mv.visitLabel(l10);
338 | mv.visitLineNumber(73, l10);
339 | mv.visitVarInsn(ALOAD, 5);
340 | mv.visitInsn(ICONST_0);
341 | mv.visitVarInsn(LLOAD, 1);
342 | mv.visitIntInsn(BIPUSH, 56);
343 | mv.visitInsn(LUSHR);
344 | mv.visitInsn(L2I);
345 | mv.visitInsn(I2B);
346 | mv.visitInsn(BASTORE);
347 | Label l11 = new Label();
348 | mv.visitLabel(l11);
349 | mv.visitLineNumber(75, l11);
350 | mv.visitInsn(ICONST_1);
351 | mv.visitVarInsn(ISTORE, 6);
352 | Label l12 = new Label();
353 | mv.visitLabel(l12);
354 | mv.visitFrame(F_APPEND, 2, new Object[]{"[B", INTEGER}, 0, null);
355 | mv.visitVarInsn(ILOAD, 6);
356 | mv.visitIntInsn(BIPUSH, 8);
357 | Label l13 = new Label();
358 | mv.visitJumpInsn(IF_ICMPGE, l13);
359 | Label l14 = new Label();
360 | mv.visitLabel(l14);
361 | mv.visitLineNumber(76, l14);
362 | mv.visitVarInsn(ALOAD, 5);
363 | mv.visitVarInsn(ILOAD, 6);
364 | mv.visitVarInsn(LLOAD, 1);
365 | mv.visitVarInsn(ILOAD, 6);
366 | mv.visitIntInsn(BIPUSH, 8);
367 | mv.visitInsn(IMUL);
368 | mv.visitInsn(LSHL);
369 | mv.visitIntInsn(BIPUSH, 56);
370 | mv.visitInsn(LUSHR);
371 | mv.visitInsn(L2I);
372 | mv.visitInsn(I2B);
373 | mv.visitInsn(BASTORE);
374 | Label l15 = new Label();
375 | mv.visitLabel(l15);
376 | mv.visitLineNumber(75, l15);
377 | mv.visitIincInsn(6, 1);
378 | mv.visitJumpInsn(GOTO, l12);
379 | mv.visitLabel(l13);
380 | mv.visitLineNumber(79, l13);
381 | mv.visitFrame(F_CHOP, 1, null, 0, null);
382 | mv.visitVarInsn(ALOAD, 3);
383 | mv.visitInsn(ICONST_2);
384 | mv.visitVarInsn(ALOAD, 4);
385 | mv.visitTypeInsn(NEW, "javax/crypto/spec/DESKeySpec");
386 | mv.visitInsn(DUP);
387 | mv.visitVarInsn(ALOAD, 5);
388 | mv.visitMethodInsn(INVOKESPECIAL, "javax/crypto/spec/DESKeySpec", "", "([B)V", false);
389 | mv.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/SecretKeyFactory", "generateSecret", "(Ljava/security/spec/KeySpec;)Ljavax/crypto/SecretKey;", false);
390 | mv.visitTypeInsn(NEW, "javax/crypto/spec/IvParameterSpec");
391 | mv.visitInsn(DUP);
392 | mv.visitIntInsn(BIPUSH, 8);
393 | mv.visitIntInsn(NEWARRAY, T_BYTE);
394 | mv.visitMethodInsn(INVOKESPECIAL, "javax/crypto/spec/IvParameterSpec", "", "([B)V", false);
395 | mv.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/Cipher", "init", "(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V", false);
396 | Label l16 = new Label();
397 | mv.visitLabel(l16);
398 | mv.visitLineNumber(81, l16);
399 | mv.visitFieldInsn(GETSTATIC, owner.name, stringField1, "[Ljava/lang/String;");
400 | mv.visitVarInsn(ILOAD, 0);
401 | mv.visitTypeInsn(NEW, "java/lang/String");
402 | mv.visitInsn(DUP);
403 | mv.visitVarInsn(ALOAD, 3);
404 | mv.visitMethodInsn(INVOKESTATIC, "java/util/Base64", "getDecoder", "()Ljava/util/Base64$Decoder;", false);
405 | mv.visitFieldInsn(GETSTATIC, owner.name, stringField2, "[Ljava/lang/String;");
406 | mv.visitVarInsn(ILOAD, 0);
407 | mv.visitInsn(AALOAD);
408 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Base64$Decoder", "decode", "(Ljava/lang/String;)[B", false);
409 | mv.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/Cipher", "doFinal", "([B)[B", false);
410 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([B)V", false);
411 | mv.visitInsn(AASTORE);
412 | mv.visitLabel(l6);
413 | mv.visitLineNumber(84, l6);
414 | mv.visitFrame(F_CHOP, 3, null, 0, null);
415 | mv.visitFieldInsn(GETSTATIC, owner.name, stringField1, "[Ljava/lang/String;");
416 | mv.visitVarInsn(ILOAD, 0);
417 | mv.visitInsn(AALOAD);
418 | mv.visitInsn(ARETURN);
419 | Label l17 = new Label();
420 | mv.visitLabel(l17);
421 |
422 | mv.visitMaxs(8, 7);
423 | mv.visitEnd();
424 |
425 | return mv;
426 | }
427 |
428 | public InsnList getClinitInstructions(ClassNode owner) {
429 | InsnList instructions = new InsnList();
430 |
431 | LabelNode l0 = new LabelNode();
432 | instructions.add(l0);
433 | instructions.add(ASMUtil.getOptimisedInt(stringCount));
434 | instructions.add(new TypeInsnNode(ANEWARRAY, "java/lang/String"));
435 | instructions.add(new FieldInsnNode(PUTSTATIC, owner.name, stringField1, "[Ljava/lang/String;"));
436 |
437 | LabelNode l1 = new LabelNode();
438 | instructions.add(l1);
439 | instructions.add(ASMUtil.getOptimisedInt(stringCount));
440 | instructions.add(new TypeInsnNode(ANEWARRAY, "java/lang/String"));
441 | instructions.add(new FieldInsnNode(PUTSTATIC, owner.name, stringField2, "[Ljava/lang/String;"));
442 |
443 | LabelNode l2 = new LabelNode();
444 | instructions.add(l2);
445 | instructions.add(new MethodInsnNode(INVOKESTATIC, owner.name, initName, "()V", false));
446 |
447 | return instructions;
448 | }
449 |
450 | public MethodNode makeInit(ClassNode owner) {
451 | MethodNode mv = new MethodNode(ACC_PRIVATE + ACC_STATIC, initName,"()V", null,null);
452 |
453 | mv.visitCode();
454 | Label l0 = new Label();
455 | Label l1 = new Label();
456 | Label l2 = new Label();
457 | //mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
458 | Label l3 = new Label();
459 | mv.visitLabel(l3);
460 | mv.visitLineNumber(95, l3);
461 | mv.visitLdcInsn(new Long(key3));
462 | mv.visitFieldInsn(PUTSTATIC, owner.name, keyField, "J");
463 | Label l4 = new Label();
464 | mv.visitLabel(l4);
465 | mv.visitLineNumber(97, l4);
466 | mv.visitFieldInsn(GETSTATIC, owner.name, keyField, "J");
467 | mv.visitLdcInsn(new Long(key2));
468 | mv.visitInsn(LXOR);
469 | mv.visitVarInsn(LSTORE, 0);
470 | mv.visitLabel(l0);
471 | mv.visitLineNumber(100, l0);
472 | mv.visitLdcInsn("DES/CBC/PKCS5Padding");
473 | mv.visitMethodInsn(INVOKESTATIC, "javax/crypto/Cipher", "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;", false);
474 | mv.visitVarInsn(ASTORE, 2);
475 | Label l5 = new Label();
476 | mv.visitLabel(l5);
477 | mv.visitLineNumber(101, l5);
478 | mv.visitLdcInsn("DES");
479 | mv.visitMethodInsn(INVOKESTATIC, "javax/crypto/SecretKeyFactory", "getInstance", "(Ljava/lang/String;)Ljavax/crypto/SecretKeyFactory;", false);
480 | mv.visitVarInsn(ASTORE, 3);
481 | Label l6 = new Label();
482 | mv.visitLabel(l6);
483 | mv.visitLineNumber(103, l6);
484 | mv.visitIntInsn(BIPUSH, 8);
485 | mv.visitIntInsn(NEWARRAY, T_BYTE);
486 | mv.visitVarInsn(ASTORE, 4);
487 | Label l7 = new Label();
488 | mv.visitLabel(l7);
489 | mv.visitLineNumber(105, l7);
490 | mv.visitVarInsn(ALOAD, 4);
491 | mv.visitInsn(ICONST_0);
492 | mv.visitVarInsn(LLOAD, 0);
493 | mv.visitIntInsn(BIPUSH, 56);
494 | mv.visitInsn(LUSHR);
495 | mv.visitInsn(L2I);
496 | mv.visitInsn(I2B);
497 | mv.visitInsn(BASTORE);
498 | Label l8 = new Label();
499 | mv.visitLabel(l8);
500 | mv.visitLineNumber(107, l8);
501 | mv.visitInsn(ICONST_1);
502 | mv.visitVarInsn(ISTORE, 5);
503 | Label l9 = new Label();
504 | mv.visitLabel(l9);
505 | mv.visitFrame(F_FULL, 5, new Object[]{LONG, "javax/crypto/Cipher", "javax/crypto/SecretKeyFactory", "[B", INTEGER}, 0, new Object[]{});
506 | mv.visitVarInsn(ILOAD, 5);
507 | mv.visitIntInsn(BIPUSH, 8);
508 | Label l10 = new Label();
509 | mv.visitJumpInsn(IF_ICMPGE, l10);
510 | Label l11 = new Label();
511 | mv.visitLabel(l11);
512 | mv.visitLineNumber(108, l11);
513 | mv.visitVarInsn(ALOAD, 4);
514 | mv.visitVarInsn(ILOAD, 5);
515 | mv.visitVarInsn(LLOAD, 0);
516 | mv.visitVarInsn(ILOAD, 5);
517 | mv.visitIntInsn(BIPUSH, 8);
518 | mv.visitInsn(IMUL);
519 | mv.visitInsn(LSHL);
520 | mv.visitIntInsn(BIPUSH, 56);
521 | mv.visitInsn(LUSHR);
522 | mv.visitInsn(L2I);
523 | mv.visitInsn(I2B);
524 | mv.visitInsn(BASTORE);
525 | Label l12 = new Label();
526 | mv.visitLabel(l12);
527 | mv.visitLineNumber(107, l12);
528 | mv.visitIincInsn(5, 1);
529 | mv.visitJumpInsn(GOTO, l9);
530 | mv.visitLabel(l10);
531 | mv.visitLineNumber(111, l10);
532 | mv.visitFrame(F_CHOP, 1, null, 0, null);
533 | mv.visitVarInsn(ALOAD, 2);
534 | mv.visitInsn(ICONST_2);
535 | mv.visitVarInsn(ALOAD, 3);
536 | mv.visitTypeInsn(NEW, "javax/crypto/spec/DESKeySpec");
537 | mv.visitInsn(DUP);
538 | mv.visitVarInsn(ALOAD, 4);
539 | mv.visitMethodInsn(INVOKESPECIAL, "javax/crypto/spec/DESKeySpec", "", "([B)V", false);
540 | mv.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/SecretKeyFactory", "generateSecret", "(Ljava/security/spec/KeySpec;)Ljavax/crypto/SecretKey;", false);
541 | mv.visitTypeInsn(NEW, "javax/crypto/spec/IvParameterSpec");
542 | mv.visitInsn(DUP);
543 | mv.visitIntInsn(BIPUSH, 8);
544 | mv.visitIntInsn(NEWARRAY, T_BYTE);
545 | mv.visitMethodInsn(INVOKESPECIAL, "javax/crypto/spec/IvParameterSpec", "", "([B)V", false);
546 | mv.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/Cipher", "init", "(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V", false);
547 | Label l13 = new Label();
548 | mv.visitLabel(l13);
549 | mv.visitLineNumber(113, l13);
550 | mv.visitInsn(ICONST_1);
551 | mv.visitVarInsn(ISTORE, 5);
552 | Label l14 = new Label();
553 | mv.visitLabel(l14);
554 | mv.visitLineNumber(115, l14);
555 | mv.visitInsn(ICONST_0);
556 | mv.visitVarInsn(ISTORE, 6);
557 | Label l15 = new Label();
558 | mv.visitLabel(l15);
559 | mv.visitFrame(F_APPEND, 2, new Object[]{INTEGER, INTEGER}, 0, null);
560 | mv.visitVarInsn(ILOAD, 6);
561 | mv.visitVarInsn(ILOAD, 5);
562 | mv.visitJumpInsn(IF_ICMPGE, l1);
563 | Label l16 = new Label();
564 | mv.visitLabel(l16);
565 | mv.visitLineNumber(116, l16);
566 | mv.visitVarInsn(ILOAD, 6);
567 |
568 | Label l17 = new Label();
569 | Label l18 = new Label();
570 | Label l19 = new Label();
571 | Label l20 = new Label();
572 | Label l21 = new Label();
573 | mv.visitTableSwitchInsn(0, 4, l20, new Label[]{l17, l18, l19, l20, l21});
574 | mv.visitLabel(l17);
575 | mv.visitLineNumber(118, l17);
576 | mv.visitFrame(F_SAME, 0, null, 0, null);
577 | for (int i = 0; i < stringCount; i++) {
578 | mv.visitFieldInsn(GETSTATIC, owner.name, stringField2, "[Ljava/lang/String;");
579 |
580 | ASMUtil.visitOptimisedInt(mv, i);
581 |
582 | mv.visitLdcInsn(encryptString(strings.get(i)));
583 | mv.visitInsn(AASTORE);
584 | }
585 |
586 | Label l22 = new Label();
587 | mv.visitLabel(l22);
588 | mv.visitLineNumber(119, l22);
589 | mv.visitJumpInsn(GOTO, l20);
590 | mv.visitLabel(l18);
591 | mv.visitLineNumber(122, l18);
592 | mv.visitFrame(F_SAME, 0, null, 0, null);
593 | // generate fake strings
594 | for (int i = 0; i < stringCount; i++) {
595 | mv.visitFieldInsn(GETSTATIC, owner.name, stringField2, "[Ljava/lang/String;");
596 |
597 | ASMUtil.visitOptimisedInt(mv, i);
598 |
599 | mv.visitLdcInsn(encryptString(strings.get(i) + StringUtil.getRandomString(2, 5, true)));
600 | mv.visitInsn(AASTORE);
601 | }
602 |
603 | Label l23 = new Label();
604 | mv.visitLabel(l23);
605 | mv.visitLineNumber(123, l23);
606 | mv.visitJumpInsn(GOTO, l20);
607 | mv.visitLabel(l19);
608 | mv.visitLineNumber(126, l19);
609 | mv.visitFrame(F_SAME, 0, null, 0, null);
610 | // TODO: this can break classes with huge amount of strings
611 | // generate completely random fake strings
612 | for (int i = 0; i < 1; i++) {
613 | mv.visitFieldInsn(GETSTATIC, owner.name, stringField2, "[Ljava/lang/String;");
614 |
615 | ASMUtil.visitOptimisedInt(mv, i);
616 |
617 | mv.visitLdcInsn(encryptString(StringUtil.getRandomString(5, 20, true)));
618 | mv.visitInsn(AASTORE);
619 | }
620 |
621 | Label l24 = new Label();
622 | mv.visitLabel(l24);
623 | mv.visitLineNumber(127, l24);
624 | mv.visitJumpInsn(GOTO, l20);
625 | mv.visitLabel(l21);
626 | mv.visitLineNumber(130, l21);
627 | mv.visitFrame(F_SAME, 0, null, 0, null);
628 | // generate completely random fake strings
629 | for (int i = 0; i < 1; i++) {
630 | mv.visitFieldInsn(GETSTATIC, owner.name, stringField2, "[Ljava/lang/String;");
631 |
632 | ASMUtil.visitOptimisedInt(mv, i);
633 |
634 | mv.visitLdcInsn(encryptString(StringUtil.getRandomString(5, 20, true)));
635 | mv.visitInsn(AASTORE);
636 | }
637 |
638 | mv.visitLabel(l20);
639 | mv.visitLineNumber(115, l20);
640 | mv.visitFrame(F_SAME, 0, null, 0, null);
641 | mv.visitIincInsn(6, 1);
642 | mv.visitJumpInsn(GOTO, l15);
643 | mv.visitLabel(l1);
644 | mv.visitLineNumber(137, l1);
645 | mv.visitFrame(F_FULL, 1, new Object[]{LONG}, 0, new Object[]{});
646 | Label l25 = new Label();
647 | mv.visitJumpInsn(GOTO, l25);
648 | mv.visitLabel(l2);
649 | mv.visitLineNumber(135, l2);
650 | mv.visitFrame(F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"});
651 | mv.visitVarInsn(ASTORE, 2);
652 | Label l26 = new Label();
653 | mv.visitLabel(l26);
654 | mv.visitLineNumber(136, l26);
655 | mv.visitVarInsn(ALOAD, 2);
656 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false);
657 | mv.visitLabel(l25);
658 | mv.visitLineNumber(138, l25);
659 | mv.visitFrame(F_SAME, 0, null, 0, null);
660 | mv.visitInsn(RETURN);
661 | Label l27 = new Label();
662 | mv.visitLabel(l27);
663 |
664 | mv.visitMaxs(6, 7);
665 | mv.visitEnd();
666 |
667 | return mv;
668 | }
669 |
670 | }
671 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/TrimMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 | import org.objectweb.asm.tree.*;
6 |
7 | import java.util.stream.Stream;
8 |
9 | public class TrimMutator extends ClassMutator {
10 |
11 | @Override
12 | public void handle(ClassWrapper wrapper) {
13 | wrapper.methods.stream()
14 | .map(m -> m.node)
15 | .forEach(method -> {
16 | InsnList insns = method.instructions;
17 |
18 | Stream.of(insns.toArray())
19 | .filter(MethodInsnNode.class::isInstance)
20 | .map(MethodInsnNode.class::cast)
21 | .forEach(insn -> {
22 | String owner = insn.owner;
23 | String name = insn.name;
24 | String desc = insn.desc;
25 |
26 | if (owner.equals("java/lang/Math")) {
27 | switch (name) {
28 | case "abs":
29 | ++counter;
30 | mutateAbs(insns, insn, owner, name, desc);
31 | break;
32 |
33 | case "max":
34 | mutateMax(insns, insn, owner, name, desc);
35 | break;
36 |
37 | case "min":
38 | mutateMin(insns, insn, owner, name, desc);
39 | break;
40 | }
41 |
42 | }
43 |
44 | });
45 | });
46 |
47 | }
48 |
49 | private void mutateMax(InsnList insns, MethodInsnNode insn, String owner, String name, String desc) {
50 | mutate(insns, insn, desc, IF_ICMPGE, IFGE);
51 | }
52 |
53 | private void mutateMin(InsnList insns, MethodInsnNode insn, String owner, String name, String desc) {
54 | mutate(insns, insn, desc, IF_ICMPLE, IFLE);
55 | }
56 |
57 | private void mutate(InsnList insns, MethodInsnNode insn, String desc, int cmp, int cmp2) {
58 | ++counter;
59 |
60 | switch (desc.charAt(desc.length() - 1)) {
61 | case 'I': {
62 | LabelNode label = new LabelNode();
63 | InsnList toAdd = new InsnList();
64 |
65 | toAdd.add(new InsnNode(DUP2));
66 | toAdd.add(new JumpInsnNode(cmp, label));
67 | toAdd.add(new InsnNode(SWAP));
68 | toAdd.add(label);
69 | toAdd.add(new InsnNode(POP));
70 |
71 | insns.insert(insn, toAdd);
72 | insns.remove(insn);
73 | break;
74 | }
75 |
76 | case 'F': {
77 | LabelNode label = new LabelNode();
78 | InsnList toAdd = new InsnList();
79 |
80 | toAdd.add(new InsnNode(DUP2));
81 | toAdd.add(new InsnNode(FCMPL));
82 | toAdd.add(new JumpInsnNode(cmp2, label));
83 | toAdd.add(new InsnNode(SWAP));
84 | toAdd.add(label);
85 | toAdd.add(new InsnNode(POP));
86 |
87 | insns.insert(insn, toAdd);
88 | insns.remove(insn);
89 | break;
90 | }
91 | }
92 | }
93 |
94 | private void mutateAbs(InsnList insns, MethodInsnNode insn, String owner, String name, String desc) {
95 | switch (desc.charAt(desc.length() - 1)) {
96 | case 'I': {
97 | LabelNode label = new LabelNode();
98 | InsnList toAdd = new InsnList();
99 |
100 | toAdd.add(new InsnNode(DUP));
101 | toAdd.add(new JumpInsnNode(IFGE, label));
102 | toAdd.add(new InsnNode(INEG));
103 | toAdd.add(label);
104 |
105 | insns.insert(insn, toAdd);
106 | insns.remove(insn);
107 | break;
108 | }
109 |
110 | case 'D': {
111 | mutateAbsNumber(insns, insn, DUP2, DCONST_0, DCMPG, DNEG);
112 | break;
113 | }
114 |
115 | case 'F': {
116 | mutateAbsNumber(insns, insn, DUP, FCONST_0, FCMPG, FNEG);
117 | break;
118 | }
119 | }
120 |
121 | }
122 |
123 | private void mutateAbsNumber(InsnList insns, MethodInsnNode insn, int dup, int const0, int cmpg, int neg) {
124 | LabelNode label = new LabelNode();
125 | InsnList toAdd = new InsnList();
126 |
127 | toAdd.add(new InsnNode(dup));
128 | toAdd.add(new InsnNode(const0));
129 | toAdd.add(new InsnNode(cmpg));
130 | toAdd.add(new JumpInsnNode(IFGE, label));
131 | toAdd.add(new InsnNode(neg));
132 | toAdd.add(label);
133 |
134 | insns.insert(insn, toAdd);
135 | insns.remove(insn);
136 | }
137 |
138 | @Override
139 | public void handleFinish() {
140 | logger.info(String.format("Trimmed %d math functions", counter));
141 | }
142 |
143 | }
144 |
145 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/crasher/BadAnnotationMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl.crasher;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 | import dev.sim0n.caesium.util.wrapper.impl.FieldWrapper;
6 | import dev.sim0n.caesium.util.wrapper.impl.MethodWrapper;
7 | import joptsimple.internal.Strings;
8 | import org.objectweb.asm.tree.AnnotationNode;
9 | import org.objectweb.asm.tree.ClassNode;
10 |
11 | import java.util.ArrayList;
12 |
13 | /**
14 | * This generates a bunch of invisible annotations which will cause procyon to be very slow
15 | */
16 | public class BadAnnotationMutator extends ClassMutator {
17 | private final String STRING = Strings.repeat('\n', 40);
18 |
19 | @Override
20 | public void handle(ClassWrapper wrapper) {
21 | ClassNode node = wrapper.node;
22 |
23 | if (node.invisibleAnnotations == null)
24 | node.invisibleAnnotations = new ArrayList<>();
25 |
26 | node.invisibleAnnotations.add(getAnnotationNode());
27 |
28 | wrapper.fields.stream()
29 | .map(f -> f.node)
30 | .forEach(f -> {
31 | if (f.invisibleAnnotations == null)
32 | f.invisibleAnnotations = new ArrayList<>();
33 |
34 | f.invisibleAnnotations.add(getAnnotationNode());
35 | });
36 |
37 | wrapper.methods.stream()
38 | .map(m -> m.node)
39 | .forEach(m -> {
40 | if (m.invisibleAnnotations == null)
41 | m.invisibleAnnotations = new ArrayList<>();
42 |
43 | m.invisibleAnnotations.add(getAnnotationNode());
44 | });
45 | }
46 |
47 | private AnnotationNode getAnnotationNode() {
48 | ++counter;
49 | return new AnnotationNode(STRING);
50 | }
51 |
52 | @Override
53 | public void handleFinish() {
54 | logger.info("Added {} annotations", counter);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/mutator/impl/crasher/ImageCrashMutator.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.mutator.impl.crasher;
2 |
3 | import dev.sim0n.caesium.mutator.ClassMutator;
4 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
5 | import org.objectweb.asm.tree.ClassNode;
6 |
7 | /**
8 | * This inserts a class that will crash almost every gui based RE tool
9 | */
10 | public class ImageCrashMutator extends ClassMutator {
11 |
12 | @Override
13 | public void handle(ClassWrapper wrapper) { }
14 |
15 | public ClassWrapper getCrashClass() {
16 | ClassNode classNode = new ClassNode();
17 |
18 | classNode.name = String.format("
= -1 && value <= 5)
13 | return new InsnNode(value + 3);
14 | else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
15 | return new IntInsnNode(BIPUSH, value);
16 | else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
17 | return new IntInsnNode(SIPUSH, value);
18 |
19 | return new LdcInsnNode(value);
20 | }
21 |
22 | public void visitOptimisedInt(MethodVisitor mv, int i) {
23 | if (i >= -1 && i <= 5)
24 | mv.visitInsn(i + 3);
25 | else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE)
26 | mv.visitIntInsn(BIPUSH, i);
27 | else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE)
28 | mv.visitIntInsn(SIPUSH, i);
29 | else
30 | mv.visitLdcInsn(i);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/ByteUtil.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util;
2 |
3 | import dev.sim0n.caesium.util.classwriter.CaesiumClassWriter;
4 | import lombok.experimental.UtilityClass;
5 | import org.objectweb.asm.ClassReader;
6 | import org.objectweb.asm.tree.ClassNode;
7 |
8 | @UtilityClass
9 | public class ByteUtil {
10 |
11 | /**
12 | * Converts a {@param bytes} to a {@link ClassNode}
13 | * @param bytes The byte array to convert into a {@link ClassNode}
14 | * @return A class node from {@param bytes}
15 | */
16 | public ClassNode parseClassBytes(byte[] bytes) {
17 | ClassReader reader = new ClassReader(bytes);
18 | ClassNode classNode = new ClassNode();
19 |
20 | reader.accept(classNode, 0);
21 |
22 | return classNode;
23 | }
24 |
25 | /**
26 | * Converts {@param classNode} to a byte array
27 | * @param classNode The class node to convert to a byte array
28 | * @return A byte array from {@param classNode}
29 | */
30 | public byte[] getClassBytes(ClassNode classNode) {
31 | CaesiumClassWriter classWriter = new CaesiumClassWriter(CaesiumClassWriter.COMPUTE_FRAMES);
32 |
33 | classWriter.newUTF8("caesium");
34 | classNode.accept(classWriter);
35 |
36 | return classWriter.toByteArray();
37 | }
38 |
39 | /**
40 | * Converts {@param bytes} to kb
41 | * @param bytes The bytes to convert
42 | * @return {@param bytes} in kb
43 | */
44 | public double bytesToKB(long bytes) {
45 | return bytes / 1024D;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/Dictionary.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util;
2 |
3 | public enum Dictionary {
4 | ABC_LOWERCASE, // a
5 | ABC, // "abc
6 | III, // IllIIIlllIIllII
7 | NUMBERS, // 321846184
8 | WACK // \n \t (this will mess with decompilers)
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/OS.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util;
2 |
3 |
4 | public enum OS {
5 | WINDOWS, MAC, UNIX
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/OSUtil.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util;
2 |
3 | import java.io.IOException;
4 |
5 |
6 | public class OSUtil {
7 |
8 | private static String osName = System.getProperty("os.name").toLowerCase();
9 |
10 | public static OS getCurrentOS() throws IOException {
11 | if (osName.contains("win")) {
12 | return OS.WINDOWS;
13 | } else if (osName.contains("mac")) {
14 | return OS.MAC;
15 | } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) {
16 | return OS.UNIX;
17 | } else {
18 | System.exit(0);
19 | return null;
20 | }
21 |
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/StringUtil.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util;
2 |
3 | import dev.sim0n.caesium.Caesium;
4 | import lombok.experimental.UtilityClass;
5 |
6 | import java.util.Random;
7 | import java.util.concurrent.ThreadLocalRandom;
8 | import java.util.stream.IntStream;
9 |
10 | @UtilityClass
11 | public class StringUtil {
12 | private final String ALPHABET = "abcdefghijklmnopqrstuvwxyz";
13 |
14 | public String getRandomString(int min, int max, boolean uppercase) {
15 | Random random = Caesium.getInstance().getRandom();
16 |
17 | StringBuilder sb = new StringBuilder();
18 |
19 | IntStream.range(0, ThreadLocalRandom.current().nextInt(min, max))
20 | .forEach(i -> {
21 | char character = ALPHABET.charAt(random.nextInt(ALPHABET.length()));
22 |
23 | if (uppercase && random.nextBoolean())
24 | character = Character.toUpperCase(character);
25 |
26 | sb.append(character);
27 | });
28 |
29 | return sb.toString();
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/VersionUtil.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util;
2 |
3 | import lombok.experimental.UtilityClass;
4 |
5 | import java.util.Properties;
6 |
7 | // Credits to @GitRowin
8 | @UtilityClass
9 | public class VersionUtil {
10 |
11 | /**
12 | * Gets the project version defined in pom.xml
13 | * @return The project version (or) debug if ran in intellij or exception is thrown
14 | */
15 | public String getVersion() {
16 | Properties properties = new Properties();
17 |
18 | try {
19 | properties.load(VersionUtil.class.getResourceAsStream("caesium.properties"));
20 |
21 | return properties.getProperty("version");
22 | } catch (Exception e) {
23 | return "DEBUG";
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/classwriter/CaesiumClassWriter.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util.classwriter;
2 |
3 | import java.lang.reflect.Modifier;
4 | import java.util.ArrayDeque;
5 | import java.util.Deque;
6 | import java.util.HashSet;
7 | import java.util.Set;
8 |
9 | import org.objectweb.asm.ClassWriter;
10 | import org.objectweb.asm.tree.ClassNode;
11 |
12 | import dev.sim0n.caesium.PreRuntime;
13 | import dev.sim0n.caesium.exception.CaesiumException;
14 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
15 |
16 | import javax.swing.*;
17 |
18 | public class CaesiumClassWriter extends ClassWriter {
19 | public CaesiumClassWriter(int flags) {
20 | super(flags);
21 | }
22 |
23 | @Override
24 | protected String getCommonSuperClass(final String type1, final String type2) {
25 | if ("java/lang/Object".equals(type1) || "java/lang/Object".equals(type2))
26 | return "java/lang/Object";
27 |
28 | String first = null;
29 | try {
30 | first = deriveCommonSuperName(type1, type2);
31 | } catch (CaesiumException e) {
32 | // TODO Auto-generated catch block
33 | e.printStackTrace();
34 | }
35 |
36 | String second = null;
37 | try {
38 | second = deriveCommonSuperName(type2, type1);
39 | } catch (CaesiumException e) {
40 | // TODO Auto-generated catch block
41 | e.printStackTrace();
42 | }
43 |
44 | if (!"java/lang/Object".equals(first))
45 | return first;
46 |
47 | if (!"java/lang/Object".equals(second))
48 | return second;
49 |
50 | try {
51 | return getCommonSuperClass(returnClazz(type1).superName, returnClazz(type2).superName);
52 | } catch (CaesiumException e) {
53 | // TODO Auto-generated catch block
54 | e.printStackTrace();
55 | }
56 |
57 | return "java/lang/Object";
58 | }
59 |
60 | private String deriveCommonSuperName(String type1, String type2) throws CaesiumException {
61 | ClassNode first = returnClazz(type1);
62 | ClassNode second = returnClazz(type2);
63 | if (isAssignableFrom(type1, type2))
64 | return type1;
65 | else if (isAssignableFrom(type2, type1))
66 | return type2;
67 | else if (Modifier.isInterface(first.access) || Modifier.isInterface(second.access))
68 | return "java/lang/Object";
69 | else {
70 | do {
71 | type1 = first.superName;
72 | first = returnClazz(type1);
73 | } while (!isAssignableFrom(type1, type2));
74 | return type1;
75 | }
76 | }
77 |
78 | private ClassNode returnClazz(String ref) throws CaesiumException {
79 | ClassWrapper clazz = PreRuntime.getClassPath().get(ref);
80 | if (clazz == null) {
81 | JOptionPane.showMessageDialog(null, "Couldn't find " + ref + " in classpath.", "Error", JOptionPane.ERROR_MESSAGE);
82 | throw new CaesiumException(ref + " does not exist in classpath!", null);
83 | }
84 |
85 | return clazz.node;
86 | }
87 |
88 | private boolean isAssignableFrom(String type1, String type2) throws CaesiumException {
89 | if ("java/lang/Object".equals(type1))
90 | return true;
91 |
92 | if (type1.equals(type2))
93 | return true;
94 |
95 | returnClazz(type1);
96 | returnClazz(type2);
97 | ClassTree firstTree = getTree(type1);
98 |
99 | if (firstTree == null)
100 | throw new CaesiumException("Could not find " + type1 + " in the built class hierarchy", null);
101 |
102 | Set allChildren = new HashSet<>();
103 | Deque toProcess = new ArrayDeque<>(firstTree.subClasses);
104 | while (!toProcess.isEmpty()) {
105 | String s = toProcess.poll();
106 | if (allChildren.add(s)) {
107 | returnClazz(s);
108 | ClassTree tempTree = getTree(s);
109 | toProcess.addAll(tempTree.subClasses);
110 | }
111 | }
112 | return allChildren.contains(type2);
113 | }
114 |
115 | public ClassTree getTree(String ref) throws CaesiumException {
116 | if (!PreRuntime.getHierarchy().containsKey(ref)) {
117 | ClassWrapper wrapper = PreRuntime.getClassPath().get(ref);
118 | PreRuntime.buildHierarchy(wrapper, null);
119 | }
120 |
121 | return PreRuntime.getHierarchy().get(ref);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/classwriter/ClassTree.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util.classwriter;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 |
6 | import dev.sim0n.caesium.util.wrapper.impl.ClassWrapper;
7 |
8 | public class ClassTree {
9 | /**
10 | * Attached ClassWrapper.
11 | */
12 | public final ClassWrapper classWrapper;
13 |
14 | /**
15 | * Names of classes this represented class inherits from.
16 | */
17 | public final Set parentClasses = new HashSet<>();
18 |
19 | /**
20 | * Names of classes this represented class is inherited by.
21 | */
22 | public final Set subClasses = new HashSet<>();
23 |
24 | /**
25 | * Creates a ClassTree object.
26 | *
27 | * @param classWrapper the ClassWraper attached to this ClassTree.
28 | */
29 | public ClassTree(ClassWrapper classWrapper) {
30 | this.classWrapper = classWrapper;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/trait/Finishable.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util.trait;
2 |
3 | public interface Finishable {
4 |
5 | void handleFinish();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/wrapper/Wrapper.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util.wrapper;
2 |
3 | import org.objectweb.asm.Opcodes;
4 |
5 | public interface Wrapper extends Opcodes {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/wrapper/impl/ClassWrapper.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util.wrapper.impl;
2 |
3 | import dev.sim0n.caesium.util.wrapper.Wrapper;
4 | import lombok.Getter;
5 | import org.objectweb.asm.tree.ClassNode;
6 | import org.objectweb.asm.tree.FieldNode;
7 | import org.objectweb.asm.tree.InsnNode;
8 | import org.objectweb.asm.tree.MethodNode;
9 |
10 | import java.util.List;
11 | import java.util.stream.Collectors;
12 |
13 | public class ClassWrapper implements Wrapper {
14 | public final ClassNode node;
15 |
16 | public final List methods;
17 | public final List fields;
18 |
19 | public final String originalName;
20 |
21 | public final boolean libraryNode;
22 |
23 | public ClassWrapper(ClassNode node) {
24 | this.node = node;
25 |
26 | methods = node.methods.stream()
27 | .map(MethodWrapper::new)
28 | .collect(Collectors.toList());
29 |
30 | fields = node.fields.stream()
31 | .map(FieldWrapper::new)
32 | .collect(Collectors.toList());
33 | this.originalName = node.name;
34 | this.libraryNode = false;
35 | }
36 |
37 | public ClassWrapper(ClassNode classNode, boolean libraryNode) {
38 | this.node = classNode;
39 | this.originalName = classNode.name;
40 | this.libraryNode = libraryNode;
41 |
42 | methods = node.methods.stream()
43 | .map(MethodWrapper::new)
44 | .collect(Collectors.toList());
45 |
46 | fields = node.fields.stream()
47 | .map(FieldWrapper::new)
48 | .collect(Collectors.toList());
49 | }
50 | /**
51 | * Gets the clinit method or creates one if it isn't present
52 | * @return The clinit method
53 | */
54 | public MethodNode getClinit() {
55 | return node.methods.stream()
56 | .filter(method -> method.name.equals(""))
57 | .findFirst()
58 | .orElseGet(() -> {
59 | MethodNode newClinit = new MethodNode(ACC_STATIC, "", "()V", null, null);
60 |
61 | newClinit.instructions.add(new InsnNode(RETURN));
62 |
63 | methods.add(new MethodWrapper(newClinit));
64 | node.methods.add(newClinit);
65 |
66 | return newClinit;
67 | });
68 | }
69 |
70 | public void addField(FieldNode fieldNode) {
71 | node.fields.add(fieldNode);
72 |
73 | fields.add(new FieldWrapper(fieldNode));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/wrapper/impl/FieldWrapper.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util.wrapper.impl;
2 |
3 | import dev.sim0n.caesium.util.wrapper.Wrapper;
4 | import lombok.Getter;
5 | import org.objectweb.asm.tree.FieldNode;
6 |
7 | public class FieldWrapper implements Wrapper {
8 | public final FieldNode node;
9 |
10 | public FieldWrapper(FieldNode node) {
11 | this.node = node;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/dev/sim0n/caesium/util/wrapper/impl/MethodWrapper.java:
--------------------------------------------------------------------------------
1 | package dev.sim0n.caesium.util.wrapper.impl;
2 |
3 | import dev.sim0n.caesium.util.wrapper.Wrapper;
4 | import lombok.Getter;
5 | import lombok.RequiredArgsConstructor;
6 | import org.objectweb.asm.commons.CodeSizeEvaluator;
7 | import org.objectweb.asm.tree.InsnList;
8 | import org.objectweb.asm.tree.MethodNode;
9 |
10 | @RequiredArgsConstructor
11 | public class MethodWrapper implements Wrapper {
12 | public final MethodNode node;
13 |
14 | public int getMaxSize() {
15 | CodeSizeEvaluator evaluator = new CodeSizeEvaluator(null);
16 |
17 | node.accept(evaluator);
18 |
19 | return evaluator.getMaxSize();
20 | }
21 |
22 | public boolean hasInstructions() {
23 | return node.instructions != null && node.instructions.size() > 0;
24 | }
25 |
26 | public InsnList getInstructions() {
27 | return node.instructions;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/resources/caesium.properties:
--------------------------------------------------------------------------------
1 | version=${project.version}
--------------------------------------------------------------------------------
/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CAESIUM_log.txt
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------