├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── me └── ANONIMUS └── deobf ├── Deobfuscator.java ├── DeobfuscatorInitializer.java ├── transformer ├── Transformer.java └── impl │ ├── AnnotationRemoverTransformer.java │ ├── DebugInfoRemoverTransformer.java │ ├── FixAccessTransformer.java │ ├── IllegalSignatureRemoverTransformer.java │ ├── IllegalVarargsRemoverTransformer.java │ ├── InvalidTryCatchRemoverTransformer.java │ ├── LocalVariableRemoverTransformer.java │ ├── UnknownAttributeRemoverTransformer.java │ ├── WatermarkTransformer.java │ ├── name │ ├── ClassNameTransformer.java │ ├── FieldNameTransformer.java │ └── MethodNameTransformer.java │ ├── optimization │ ├── GotoInlinerTransformer.java │ └── NopRemoverTransformer.java │ ├── shrinking │ ├── InnerClassTransformer.java │ ├── LineNumberRemoverTransformer.java │ ├── SignatureRemoverTransformer.java │ ├── SourceDebugRemoverTransformer.java │ └── SourceFileRemoverTransformer.java │ └── string │ ├── AllatoriStringTransformer.java │ ├── AlpheratzTeamStringTransformer.java │ ├── DashoStringTransformer.java │ ├── SuperBlaubeere27StringTransformer.java │ └── qProtectStringTransformer.java └── util └── BytecodeUtils.java /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ikos3k 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 | # Java Deobfuscator 2 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | JavaDeobfuscator 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.projectlombok 26 | lombok 27 | 1.18.16 28 | provided 29 | 30 | 31 | org.ow2.asm 32 | asm 33 | 7.1 34 | 35 | 36 | org.ow2.asm 37 | asm-analysis 38 | 7.1 39 | 40 | 41 | org.ow2.asm 42 | asm-commons 43 | 7.1 44 | 45 | 46 | org.ow2.asm 47 | asm-tree 48 | 7.1 49 | 50 | 51 | org.ow2.asm 52 | asm-util 53 | 7.1 54 | 55 | 56 | commons-io 57 | commons-io 58 | 2.6 59 | 60 | 61 | org.apache.commons 62 | commons-lang3 63 | 3.11 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/Deobfuscator.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf; 2 | 3 | import lombok.Getter; 4 | import me.ANONIMUS.deobf.transformer.Transformer; 5 | import me.ANONIMUS.deobf.transformer.impl.WatermarkTransformer; 6 | import me.ANONIMUS.deobf.util.BytecodeUtils; 7 | import org.apache.commons.io.IOUtils; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.tree.ClassNode; 10 | import org.objectweb.asm.tree.analysis.AnalyzerException; 11 | 12 | import java.io.File; 13 | import java.io.FileOutputStream; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.util.*; 17 | import java.util.jar.JarEntry; 18 | import java.util.jar.JarFile; 19 | import java.util.jar.JarOutputStream; 20 | 21 | @Getter 22 | public class Deobfuscator { 23 | private static Deobfuscator instance; 24 | 25 | private final List transformers; 26 | private final Map files; 27 | private final List classes; 28 | 29 | public Deobfuscator() { 30 | instance = this; 31 | 32 | this.files = new HashMap<>(); 33 | this.classes = new ArrayList<>(); 34 | this.transformers = new ArrayList<>(); 35 | } 36 | 37 | public void run(String[] args) { 38 | if (args.length == 0) { 39 | System.err.println("[ERROR] Input file must be specified"); 40 | return; 41 | } 42 | 43 | String input = args[0]; 44 | if (!input.endsWith(".jar")) { 45 | input += ".jar"; 46 | } 47 | 48 | File in = new File(input); 49 | if (!in.exists()) { 50 | System.err.println("[ERROR] Input file not found"); 51 | return; 52 | } 53 | 54 | File out = new File(input.substring(0, input.length() - ".jar".length()) + "Deobf.jar"); 55 | if (out.exists() && !out.delete()) { 56 | System.err.println("[ERROR] Could not delete out file"); 57 | return; 58 | } 59 | 60 | loadJar(in); 61 | System.out.println("[INFO] Successful loaded " + in.getName() + " [" + classes.size() + " class] [" + files.size() + " files]"); 62 | 63 | transformers.add(new WatermarkTransformer()); 64 | 65 | System.out.println(); 66 | transformers.forEach(transformer -> { 67 | System.out.println(transformer.getClass().getSimpleName() + " running..."); 68 | try { 69 | transformer.visit(classes); 70 | } catch (AnalyzerException e) { 71 | System.err.println("[ERROR] An error has occurred in " + transformer.getClass().getSimpleName()); 72 | e.printStackTrace(); 73 | } 74 | }); 75 | 76 | System.out.println(); 77 | System.out.println("[INFO] Finished, saving the file..."); 78 | saveJar(out); 79 | } 80 | 81 | private void loadJar(File file) { 82 | try (JarFile jarFile = new JarFile(file)) { 83 | final Enumeration entries = jarFile.entries(); 84 | while (entries.hasMoreElements()) { 85 | final JarEntry jarEntry = entries.nextElement(); 86 | try (InputStream inputStream = jarFile.getInputStream(jarEntry)) { 87 | final byte[] bytes = IOUtils.toByteArray(inputStream); 88 | if (!jarEntry.getName().endsWith(".class")) { 89 | files.put(jarEntry.getName(), bytes); 90 | continue; 91 | } 92 | 93 | try { 94 | if (BytecodeUtils.checkClassVerify(bytes)) { 95 | final ClassNode classNode = new ClassNode(); 96 | new ClassReader(bytes).accept(classNode, ClassReader.EXPAND_FRAMES); 97 | this.classes.add(classNode); 98 | } 99 | } catch (Exception e) { 100 | System.err.println("[ERROR] There was an error loading " + jarEntry.getName()); 101 | } 102 | } 103 | } 104 | } catch (IOException ex) { 105 | ex.printStackTrace(); 106 | } 107 | } 108 | 109 | private void saveJar(File out) { 110 | try (JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(out))) { 111 | for (ClassNode classNode : this.classes) { 112 | jarOutputStream.putNextEntry(new JarEntry(classNode.name + ".class")); 113 | jarOutputStream.write(BytecodeUtils.toByteArray(classNode)); 114 | jarOutputStream.closeEntry(); 115 | } 116 | 117 | for (Map.Entry entry : this.files.entrySet()) { 118 | jarOutputStream.putNextEntry(new JarEntry(entry.getKey())); 119 | jarOutputStream.write(entry.getValue()); 120 | jarOutputStream.closeEntry(); 121 | } 122 | System.out.println("[INFO] Successful saved " + out.getName()); 123 | } catch (Exception e) { 124 | e.printStackTrace(); 125 | } 126 | } 127 | 128 | public Deobfuscator addTransformer(Transformer transformer) { 129 | if (!transformers.contains(transformer)) 130 | transformers.add(transformer); 131 | return this; 132 | } 133 | 134 | public static Deobfuscator getInstance() { 135 | return instance; 136 | } 137 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/DeobfuscatorInitializer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf; 2 | 3 | public class DeobfuscatorInitializer { 4 | public static void main(String[] args) { 5 | new Deobfuscator() 6 | // .addTransformer(new LocalVariableRemoverTransformer()) 7 | // .addTransformer(new SignatureRemoverTransformer()) 8 | // .addTransformer(new FieldNameTransformer()) 9 | .run(args); 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/Transformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer; 2 | 3 | import me.ANONIMUS.deobf.util.BytecodeUtils; 4 | import org.objectweb.asm.Opcodes; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.objectweb.asm.tree.analysis.AnalyzerException; 7 | 8 | import java.util.List; 9 | 10 | public abstract class Transformer extends BytecodeUtils implements Opcodes { 11 | public abstract void visit(List classMap) throws AnalyzerException; 12 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/AnnotationRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | import java.util.List; 7 | 8 | public class AnnotationRemoverTransformer extends Transformer { 9 | @Override 10 | public void visit(List classMap) { 11 | classMap.forEach(classNode -> { 12 | classNode.invisibleAnnotations = null; 13 | classNode.visibleAnnotations = null; 14 | 15 | classNode.methods.forEach(methodNode -> { 16 | methodNode.invisibleAnnotations = null; 17 | methodNode.visibleAnnotations = null; 18 | }); 19 | 20 | classNode.fields.forEach(fieldNode -> { 21 | fieldNode.invisibleAnnotations = null; 22 | fieldNode.visibleAnnotations = null; 23 | }); 24 | }); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/DebugInfoRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.Deobfuscator; 4 | import me.ANONIMUS.deobf.transformer.Transformer; 5 | import me.ANONIMUS.deobf.util.BytecodeUtils; 6 | import org.objectweb.asm.ClassReader; 7 | import org.objectweb.asm.tree.ClassNode; 8 | import org.objectweb.asm.tree.analysis.AnalyzerException; 9 | 10 | import java.util.List; 11 | 12 | public class DebugInfoRemoverTransformer extends Transformer { 13 | @Override 14 | public void visit(List classMap) throws AnalyzerException { 15 | classMap.forEach(classNode -> { 16 | ClassNode classNodeCopy = new ClassNode(); 17 | new ClassReader(BytecodeUtils.toByteArray(classNode)).accept(classNodeCopy, ClassReader.SKIP_DEBUG); 18 | 19 | Deobfuscator.getInstance().getClasses().remove(classNode); 20 | Deobfuscator.getInstance().getClasses().add(classNode); 21 | }); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/FixAccessTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | import java.util.List; 7 | 8 | public class FixAccessTransformer extends Transformer { 9 | @Override 10 | public void visit(List classMap) { 11 | classMap.forEach(classNode -> { 12 | classNode.access = fixAccess(classNode.access); 13 | classNode.methods.forEach(methodNode -> { 14 | methodNode.access = fixAccess(methodNode.access); 15 | if (methodNode.parameters != null) { 16 | methodNode.parameters.forEach(pn -> pn.access = fixAccess(pn.access)); 17 | } 18 | }); 19 | classNode.fields.forEach(fieldNode -> fieldNode.access = fixAccess(fieldNode.access)); 20 | }); 21 | } 22 | 23 | public int fixAccess(int access) { 24 | int acc = ACC_PUBLIC; 25 | if (isStatic(access)) { 26 | acc = addAccess(acc, ACC_STATIC); 27 | } 28 | if (isAbstract(access)) { 29 | acc = addAccess(acc, ACC_ABSTRACT); 30 | } 31 | if (isInterface(access)) { 32 | acc = addAccess(acc, ACC_INTERFACE); 33 | } 34 | return acc; 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/IllegalSignatureRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.util.CheckClassAdapter; 6 | 7 | import java.util.List; 8 | 9 | public class IllegalSignatureRemoverTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) { 12 | classMap.forEach(classNode -> { 13 | if (classNode.signature != null) { 14 | try { 15 | CheckClassAdapter.checkClassSignature(classNode.signature); 16 | } catch (IllegalArgumentException | StringIndexOutOfBoundsException ignored) { 17 | classNode.signature = null; 18 | } 19 | } 20 | classNode.methods.forEach(methodNode -> { 21 | if (methodNode.signature != null) { 22 | try { 23 | CheckClassAdapter.checkMethodSignature(methodNode.signature); 24 | } catch (IllegalArgumentException | StringIndexOutOfBoundsException ignored) { 25 | methodNode.signature = null; 26 | } 27 | } 28 | }); 29 | classNode.fields.forEach(fieldNode -> { 30 | if (fieldNode.signature != null) { 31 | try { 32 | CheckClassAdapter.checkFieldSignature(fieldNode.signature); 33 | } catch (IllegalArgumentException | StringIndexOutOfBoundsException ignored) { 34 | fieldNode.signature = null; 35 | } 36 | } 37 | }); 38 | }); 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/IllegalVarargsRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.Type; 5 | import org.objectweb.asm.tree.ClassNode; 6 | 7 | import java.util.List; 8 | 9 | public class IllegalVarargsRemoverTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) { 12 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> { 13 | Type[] args = Type.getArgumentTypes(methodNode.desc); 14 | if (args.length > 0 && args[args.length - 1].getSort() != Type.ARRAY) { 15 | methodNode.access = removeAccess(methodNode.access, ACC_VARARGS); 16 | } 17 | })); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/InvalidTryCatchRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.objectweb.asm.tree.analysis.Analyzer; 7 | import org.objectweb.asm.tree.analysis.AnalyzerException; 8 | import org.objectweb.asm.tree.analysis.BasicInterpreter; 9 | import org.objectweb.asm.tree.analysis.Frame; 10 | 11 | import java.util.List; 12 | 13 | public class InvalidTryCatchRemoverTransformer extends Transformer { 14 | @Override 15 | public void visit(List classMap) throws AnalyzerException { 16 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> { 17 | methodNode.tryCatchBlocks.removeIf(tcb -> tcb.start == tcb.end || methodNode.instructions.indexOf(tcb.start) >= methodNode.instructions.indexOf(tcb.end)); 18 | 19 | Analyzer analyzer = new Analyzer<>(new BasicInterpreter()); 20 | try { 21 | analyzer.analyze(classNode.name, methodNode); 22 | } catch (AnalyzerException ignored) { 23 | return; 24 | } 25 | Frame[] frames = analyzer.getFrames(); 26 | AbstractInsnNode[] insns = methodNode.instructions.toArray(); 27 | for (int i = 0; i < frames.length; i++) { 28 | AbstractInsnNode insn = insns[i]; 29 | if (frames[i] == null && insn.getType() != AbstractInsnNode.LABEL) { 30 | methodNode.instructions.remove(insn); 31 | insns[i] = null; 32 | } 33 | } 34 | })); 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/LocalVariableRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | import java.util.List; 7 | 8 | public class LocalVariableRemoverTransformer extends Transformer { 9 | @Override 10 | public void visit(List classMap) { 11 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> methodNode.localVariables = null)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/UnknownAttributeRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.Attribute; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.objectweb.asm.tree.analysis.AnalyzerException; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public class UnknownAttributeRemoverTransformer extends Transformer { 12 | @Override 13 | public void visit(List classMap) throws AnalyzerException { 14 | Arrays.stream(classMap.toArray(new ClassNode[0])).filter(classNode -> classNode.attrs != null).forEach(classNode -> classNode.attrs.stream().filter(Attribute::isUnknown).forEach(attr -> classNode.attrs.remove(attr))); 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/WatermarkTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | 7 | import java.util.List; 8 | 9 | public class WatermarkTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) { 12 | final MethodNode methodNode = createMethod(); 13 | classMap.stream().filter(classNode -> !isInterface(classNode.access) && !isAbstract(classNode.access) && !isAnnotation(classNode.access)).forEach(classNode -> classNode.methods.add(methodNode)); 14 | } 15 | 16 | private MethodNode createMethod() { 17 | final MethodNode methodNode = new MethodNode(ACC_PROTECTED, "hi", createDescription(DESC_STRING, DESC_EMPTY), null, null); 18 | methodNode.visitCode(); 19 | methodNode.visitLdcInsn("github: " + "https://github.com/Ikos3k"); 20 | methodNode.visitVarInsn(ASTORE, 0); 21 | methodNode.visitLdcInsn("deobf by ANONIMUS(Ikos3k)"); 22 | methodNode.visitInsn(ARETURN); 23 | methodNode.visitEnd(); 24 | return methodNode; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/name/ClassNameTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.name; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | import java.util.Collections; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.stream.Collectors; 11 | 12 | public class ClassNameTransformer extends Transformer { 13 | @Override 14 | public void visit(List classMap) { 15 | Map remap = new HashMap<>(); 16 | 17 | List keys = classMap.stream().map(c -> c.name).collect(Collectors.toList()); 18 | Collections.shuffle(keys); 19 | int i = 0; 20 | for (String key : keys) { 21 | ClassNode cn = getClassNode(key, classMap); 22 | if (!isMainClass(cn)) { 23 | remap.put(cn.name, "class_" + i); 24 | i++; 25 | } 26 | } 27 | applyMappings(classMap, remap); 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/name/FieldNameTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.name; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.FieldNode; 6 | 7 | import java.util.*; 8 | import java.util.stream.Collectors; 9 | 10 | public class FieldNameTransformer extends Transformer { 11 | @Override 12 | public void visit(List classMap) { 13 | Map remap = new HashMap<>(); 14 | 15 | List fields = new ArrayList<>(); 16 | classMap.forEach(c -> fields.addAll(c.fields)); 17 | Collections.shuffle(fields); 18 | int i = 0; 19 | for (FieldNode f : fields) { 20 | Stack nodeStack = new Stack<>(); 21 | nodeStack.add(getOwner(f, classMap)); 22 | while (nodeStack.size() > 0) { 23 | ClassNode node = nodeStack.pop(); 24 | remap.put(node.name + "." + f.name, "field_" + i); 25 | nodeStack.addAll(classMap.stream(). 26 | filter(cn -> cn.superName.equals(node.name)). 27 | collect(Collectors.toList())); 28 | } 29 | i++; 30 | } 31 | applyMappings(classMap, remap); 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/name/MethodNameTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.name; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | 7 | import java.util.*; 8 | 9 | public class MethodNameTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) { 12 | Map remap = new HashMap<>(); 13 | 14 | List methods = new LinkedList<>(); 15 | classMap.forEach(c -> methods.addAll(c.methods)); 16 | 17 | Collections.shuffle(methods); 18 | int i = 0; 19 | methods: 20 | for (MethodNode m : methods) { 21 | ClassNode owner = getOwner(m, classMap); 22 | 23 | if (isInitializer(m) || isNative(m.access) || isMainMethod(m)) { 24 | continue; 25 | } 26 | Stack nodeStack = new Stack<>(); 27 | nodeStack.add(owner); 28 | while (nodeStack.size() > 0) { 29 | ClassNode node = nodeStack.pop(); 30 | if (node != owner && getMethod(node, m.name, m.desc) != null) 31 | continue methods; 32 | ClassNode parent = getClassNode(node.superName, classMap); 33 | if (parent != null) 34 | nodeStack.push(parent); 35 | Set interfaces = new HashSet<>(); 36 | for (String str : node.interfaces.toArray(new String[0])) { 37 | ClassNode cn = getClassNode(str, classMap); 38 | if (cn != null) { 39 | interfaces.add(cn); 40 | } 41 | } 42 | nodeStack.addAll(interfaces); 43 | } 44 | nodeStack.add(owner); 45 | while (nodeStack.size() > 0) { 46 | ClassNode node = nodeStack.pop(); 47 | remap.put(node.name + '.' + m.name + m.desc, "method_" + i); 48 | classMap.stream().filter(c -> c.superName.equals(node.name) || c.interfaces.contains(node.name)).forEach(nodeStack::push); 49 | } 50 | i++; 51 | } 52 | applyMappings(classMap, remap); 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/optimization/GotoInlinerTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.optimization; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.objectweb.asm.tree.JumpInsnNode; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public class GotoInlinerTransformer extends Transformer { 12 | @Override 13 | public void visit(List classMap) { 14 | classMap.forEach(classNode -> 15 | classNode.methods.forEach(methodNode -> 16 | Arrays.stream(methodNode.instructions.toArray()).filter(abstractInsnNode -> abstractInsnNode.getOpcode() == GOTO).forEach(abstractInsnNode -> { 17 | final JumpInsnNode gotoJump = (JumpInsnNode) abstractInsnNode; 18 | final AbstractInsnNode insnAfterTarget = gotoJump.label.getNext(); 19 | if (insnAfterTarget == null) 20 | return; 21 | 22 | if (insnAfterTarget.getOpcode() != GOTO) 23 | return; 24 | 25 | gotoJump.label = ((JumpInsnNode) insnAfterTarget).label; 26 | }) 27 | ) 28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/optimization/NopRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.optimization; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class NopRemoverTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) { 12 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> Arrays.stream(methodNode.instructions.toArray()).filter(insnNode -> insnNode.getOpcode() == NOP).forEach(insnNode -> methodNode.instructions.remove(insnNode)))); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/shrinking/InnerClassTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.shrinking; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | import java.util.List; 7 | 8 | public class InnerClassTransformer extends Transformer { 9 | @Override 10 | public void visit(List classMap) { 11 | classMap.forEach(classNode -> { 12 | classNode.outerClass = null; 13 | classNode.outerMethod = null; 14 | classNode.outerMethodDesc = null; 15 | classNode.innerClasses.clear(); 16 | }); 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/shrinking/LineNumberRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.shrinking; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.objectweb.asm.tree.LineNumberNode; 7 | 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | public class LineNumberRemoverTransformer extends Transformer { 12 | @Override 13 | public void visit(List classMap) { 14 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> { 15 | Iterator it = methodNode.instructions.iterator(); 16 | while (it.hasNext()) { 17 | if (it.next() instanceof LineNumberNode) { 18 | it.remove(); 19 | } 20 | } 21 | })); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/shrinking/SignatureRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.shrinking; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.analysis.AnalyzerException; 6 | 7 | import java.util.List; 8 | 9 | public class SignatureRemoverTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) throws AnalyzerException { 12 | classMap.forEach(classNode -> { 13 | classNode.signature = null; 14 | classNode.methods.forEach(methodNode -> methodNode.signature = null); 15 | classNode.fields.forEach(fieldNode -> fieldNode.signature = null); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/shrinking/SourceDebugRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.shrinking; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.analysis.AnalyzerException; 6 | 7 | import java.util.List; 8 | 9 | public class SourceDebugRemoverTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) throws AnalyzerException { 12 | classMap.forEach(classNode -> classNode.sourceDebug = null); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/shrinking/SourceFileRemoverTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.shrinking; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.analysis.AnalyzerException; 6 | 7 | import java.util.List; 8 | 9 | public class SourceFileRemoverTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) throws AnalyzerException { 12 | classMap.forEach(classNode -> classNode.sourceFile = null); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/string/AllatoriStringTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.string; 2 | 3 | import lombok.AllArgsConstructor; 4 | import me.ANONIMUS.deobf.transformer.Transformer; 5 | import org.objectweb.asm.tree.AbstractInsnNode; 6 | import org.objectweb.asm.tree.ClassNode; 7 | import org.objectweb.asm.tree.LdcInsnNode; 8 | 9 | import java.util.List; 10 | 11 | @AllArgsConstructor 12 | public class AllatoriStringTransformer extends Transformer { 13 | private final int mode; 14 | private final boolean removeWatermark; 15 | 16 | @Override 17 | public void visit(List classMap) { 18 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> { 19 | AbstractInsnNode[] abstractInsnNodes = methodNode.instructions.toArray(); 20 | for (AbstractInsnNode abstractInsnNode : abstractInsnNodes) { 21 | if (abstractInsnNode.getType() == AbstractInsnNode.LDC_INSN) { 22 | LdcInsnNode ldcInsnNode = (LdcInsnNode) abstractInsnNode; 23 | if (ldcInsnNode.cst instanceof String) { 24 | if (ldcInsnNode.getNext() != null && ldcInsnNode.getNext().getOpcode() == INVOKESTATIC) { 25 | ldcInsnNode.cst = getDecryptedString((String) ldcInsnNode.cst, mode); 26 | methodNode.instructions.remove(ldcInsnNode.getNext()); 27 | } 28 | } 29 | } 30 | } 31 | })); 32 | if (removeWatermark) { 33 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> { 34 | if (isMainMethod(methodNode)) { 35 | AbstractInsnNode[] abstractInsnNodes = methodNode.instructions.toArray(); 36 | for (AbstractInsnNode abstractInsnNode : abstractInsnNodes) { 37 | if (abstractInsnNode instanceof LdcInsnNode) { 38 | LdcInsnNode ldcInsnNode = (LdcInsnNode) abstractInsnNode; 39 | if (ldcInsnNode.cst instanceof String && ldcInsnNode.cst.equals("\n################################################\n# #\n# ## # # ## ### ### ## ### #\n# # # # # # # # # # # # # #\n# ### # # ### # # # ## # #\n# # # ### ### # # # ### # # ### #\n# #\n# Obfuscation by Allatori Obfuscator v6.5 DEMO #\n# #\n# http://www.allatori.com #\n# #\n################################################\n")) { 40 | methodNode.instructions.remove(ldcInsnNode.getPrevious()); 41 | methodNode.instructions.remove(ldcInsnNode.getNext().getNext()); 42 | methodNode.instructions.remove(ldcInsnNode.getNext()); 43 | methodNode.instructions.remove(ldcInsnNode); 44 | } 45 | } 46 | } 47 | } 48 | })); 49 | } 50 | } 51 | 52 | private String getDecryptedString(String string, int mode) { 53 | switch (mode) { 54 | case 0: { 55 | return decrypt0(string); 56 | } 57 | case 1: { 58 | return decrypt1(string); 59 | } 60 | case 2: { 61 | return decrypt2(string); 62 | } 63 | case 3: { 64 | return decrypt3(string); 65 | } 66 | case 4: { 67 | return decrypt4(string); 68 | } 69 | case 5: { 70 | return decrypt5(string); 71 | } 72 | case 6: { 73 | return decrypt6(string); 74 | } 75 | case 7: { 76 | return decrypt7(string); 77 | } 78 | } 79 | return string; 80 | } 81 | 82 | private String decrypt0(String s) { 83 | int n2; 84 | char[] array = new char[s.length()]; 85 | int i = n2 = array.length - 1; 86 | int c = 115; 87 | while (i >= 0) { 88 | int n3 = n2; 89 | char c2 = (char) (s.charAt(n2) ^ c); 90 | char c3 = (char) ((char) (n3 ^ c) & 0x3F); 91 | array[n3] = c2; 92 | if (--n2 < 0) break; 93 | int n4 = n2; 94 | char c4 = (char) (s.charAt(n2) ^ c3); 95 | c = (char) ((char) (n4 ^ c3) & 0x3F); 96 | array[n4] = c4; 97 | i = --n2; 98 | } 99 | return new String(array); 100 | } 101 | 102 | private String decrypt1(String s) { 103 | int n2; 104 | char[] array = new char[s.length()]; 105 | int i = n2 = array.length - 1; 106 | int c = 116; 107 | while (i >= 0) { 108 | int n3 = n2; 109 | char c2 = (char) (s.charAt(n2) ^ c); 110 | char c3 = (char) ((char) (n3 ^ c) & 0x3F); 111 | array[n3] = c2; 112 | if (--n2 < 0) break; 113 | int n4 = n2; 114 | char c4 = (char) (s.charAt(n2) ^ c3); 115 | c = (char) ((char) (n4 ^ c3) & 0x3F); 116 | array[n4] = c4; 117 | i = --n2; 118 | } 119 | return new String(array); 120 | } 121 | 122 | private String decrypt2(String s) { 123 | int i = s.length(); 124 | char[] a = new char[i]; 125 | int i0 = i - 1; 126 | while (i0 >= 0) { 127 | int i1 = s.charAt(i0); 128 | int i2 = i0 + -1; 129 | int i3 = (char) (i1 ^ 105); 130 | a[i0] = (char) i3; 131 | if (i2 >= 0) { 132 | i0 = i2 + -1; 133 | int i4 = s.charAt(i2); 134 | int i5 = (char) (i4 ^ 59); 135 | a[i2] = (char) i5; 136 | } 137 | } 138 | return new String(a); 139 | } 140 | 141 | private String decrypt3(String s) { 142 | int n = s.length(); 143 | int n2 = n - 1; 144 | char[] arrc = new char[n]; 145 | int n3 = 5 << 3 ^ 2; 146 | int n4 = n2; 147 | int n5 = (3 ^ 5) << 4 ^ 2 << 1; 148 | while (n4 >= 0) { 149 | int n6 = n2--; 150 | arrc[n6] = (char) (s.charAt(n6) ^ n5); 151 | if (n2 < 0) break; 152 | int n7 = n2--; 153 | arrc[n7] = (char) (s.charAt(n7) ^ n3); 154 | n4 = n2; 155 | } 156 | return new String(arrc); 157 | } 158 | 159 | private String decrypt4(String a) { 160 | final int n = (0x2 ^ 0x5) << 4; 161 | final int n2 = 2; 162 | final int n3 = n ^ (n2 << n2 ^ 0x1); 163 | final int n4 = (0x2 ^ 0x5) << 3 ^ 0x2; 164 | final int length = a.length(); 165 | final char[] array = new char[length]; 166 | int n5; 167 | int i = n5 = length - 1; 168 | final char c = (char) n4; 169 | while (i >= 0) { 170 | final int n7 = n5; 171 | final char char1 = a.charAt(n7); 172 | --n5; 173 | array[n7] = (char) (char1 ^ n3); 174 | if (n5 < 0) { 175 | break; 176 | } 177 | final int n8 = n5--; 178 | array[n8] = (char) (a.charAt(n8) ^ c); 179 | i = n5; 180 | } 181 | return new String(array); 182 | } 183 | 184 | private String decrypt5(String a) { 185 | final int n = 4; 186 | final int n2 = n << n ^ 2 << 1; 187 | final int n3 = (0x3 ^ 0x5) << 4 ^ 0x3; 188 | final int length = a.length(); 189 | final char[] array = new char[length]; 190 | int n4; 191 | int i = n4 = length - 1; 192 | final char c = (char) n3; 193 | while (i >= 0) { 194 | final int n6 = n4; 195 | final char char1 = a.charAt(n6); 196 | --n4; 197 | array[n6] = (char) (char1 ^ n2); 198 | if (n4 < 0) { 199 | break; 200 | } 201 | final int n7 = n4--; 202 | array[n7] = (char) (a.charAt(n7) ^ c); 203 | i = n4; 204 | } 205 | return new String(array); 206 | } 207 | 208 | private String decrypt6(String a) { 209 | final int n = 4; 210 | final int n2 = n << n ^ 3 << 1; 211 | final int n3 = (0x3 ^ 0x5) << 4; 212 | final int n4 = 1; 213 | final int n5 = n3 ^ n4 << n4; 214 | final int length = a.length(); 215 | final char[] array = new char[length]; 216 | int n6; 217 | int i = n6 = length - 1; 218 | final char c = (char) n5; 219 | while (i >= 0) { 220 | final int n8 = n6; 221 | final char char1 = a.charAt(n8); 222 | --n6; 223 | array[n8] = (char) (char1 ^ n2); 224 | if (n6 < 0) { 225 | break; 226 | } 227 | final int n9 = n6--; 228 | array[n9] = (char) (a.charAt(n9) ^ c); 229 | i = n6; 230 | } 231 | return new String(array); 232 | } 233 | 234 | private String decrypt7(String a) { 235 | final int n = (0x3 ^ 0x5) << 3 ^ 0x5; 236 | final int n2 = 4; 237 | final int n3 = n2 << n2 ^ (0x3 ^ 0x5) << 1; 238 | final int length = a.length(); 239 | final char[] array = new char[length]; 240 | int n4; 241 | int i = n4 = length - 1; 242 | final char c = (char) n3; 243 | while (i >= 0) { 244 | final int n6 = n4; 245 | final char char1 = a.charAt(n6); 246 | --n4; 247 | array[n6] = (char) (char1 ^ n); 248 | if (n4 < 0) { 249 | break; 250 | } 251 | final int n7 = n4--; 252 | array[n7] = (char) (a.charAt(n7) ^ c); 253 | i = n4; 254 | } 255 | return new String(array); 256 | } 257 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/string/AlpheratzTeamStringTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.string; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.Type; 5 | import org.objectweb.asm.tree.*; 6 | 7 | import java.util.List; 8 | 9 | public class AlpheratzTeamStringTransformer extends Transformer { 10 | @Override 11 | public void visit(List classMap) { 12 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> { 13 | AbstractInsnNode[] abstractInsnNodes = methodNode.instructions.toArray(); 14 | for (AbstractInsnNode abstractInsnNode : abstractInsnNodes) { 15 | if (abstractInsnNode.getType() == AbstractInsnNode.LDC_INSN) { 16 | LdcInsnNode ldcInsnNode = (LdcInsnNode) abstractInsnNode; 17 | AbstractInsnNode abInsnNodeNEXT = ldcInsnNode.getNext(); 18 | if (ldcInsnNode.cst instanceof String && abInsnNodeNEXT.getType() == AbstractInsnNode.METHOD_INSN) { 19 | final Type cType = getClassType(classNode, ((MethodInsnNode) abInsnNodeNEXT).name); 20 | if (cType != null) { 21 | String className = cType.toString().replaceFirst("L", "").replace(";", ""); 22 | ldcInsnNode.cst = decrypt((String) ldcInsnNode.cst, computeConstantPoolSize(getClassNode(className, classMap))); 23 | methodNode.instructions.remove(abInsnNodeNEXT); 24 | } 25 | } 26 | } 27 | } 28 | })); 29 | } 30 | 31 | private Type getClassType(ClassNode classNode, String name) { 32 | for (MethodNode mn : classNode.methods) { 33 | if (mn.name.equals(name)) { 34 | for (AbstractInsnNode ab : mn.instructions.toArray()) { 35 | if (ab.getType() == AbstractInsnNode.METHOD_INSN && ab.getNext().getType() == AbstractInsnNode.LDC_INSN) { 36 | LdcInsnNode ldcInsnNode = (LdcInsnNode) ab.getNext(); 37 | if (((MethodInsnNode) ab).owner.equals("sun/misc/SharedSecrets") && ldcInsnNode.cst instanceof Type) { 38 | return (Type) ldcInsnNode.cst; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | return null; 45 | } 46 | 47 | private String decrypt(final String str, int key) { 48 | final char[] c = str.toCharArray(); 49 | for (int i = 0; i < c.length; i++) { 50 | c[i] ^= key; 51 | } 52 | return new String(c); 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/string/DashoStringTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.string; 2 | 3 | import lombok.AllArgsConstructor; 4 | import me.ANONIMUS.deobf.transformer.Transformer; 5 | import org.objectweb.asm.tree.AbstractInsnNode; 6 | import org.objectweb.asm.tree.ClassNode; 7 | import org.objectweb.asm.tree.LdcInsnNode; 8 | import org.objectweb.asm.tree.analysis.AnalyzerException; 9 | 10 | import java.util.List; 11 | 12 | @AllArgsConstructor 13 | public class DashoStringTransformer extends Transformer { 14 | private final int mode; 15 | 16 | @Override 17 | public void visit(List classMap) throws AnalyzerException { 18 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> { 19 | for (AbstractInsnNode insn : methodNode.instructions.toArray()) { 20 | if (insn instanceof LdcInsnNode && ((LdcInsnNode) insn).cst instanceof String && insn.getNext() != null && isIntInsn(insn.getNext())) { 21 | AbstractInsnNode abstractInsnNodeNEXT = insn.getNext(); 22 | methodNode.instructions.set(abstractInsnNodeNEXT, new LdcInsnNode(getDecryptedString((String) ((LdcInsnNode) insn).cst, getIntNumber(abstractInsnNodeNEXT), mode))); 23 | methodNode.instructions.remove(insn); 24 | } 25 | } 26 | })); 27 | } 28 | 29 | private String getDecryptedString(String string, int key, int mode) { 30 | switch (mode) { 31 | case 0: { 32 | return decrypt0(string, key); 33 | } 34 | case 1: { 35 | return decrypt1(string, key); 36 | } 37 | } 38 | return string; 39 | } 40 | 41 | private String decrypt0(String string, int n) { 42 | char[] cArray = string.toCharArray(); 43 | int n2 = cArray.length; 44 | int n3 = 0; 45 | int n4 = (4 << 4 + 1) - 1 ^ 0x20; 46 | while (n3 != n2) { 47 | int n5 = n3++; 48 | int n6 = n & n4 ^ cArray[n5]; 49 | ++n; 50 | cArray[n5] = (char) n6; 51 | } 52 | return String.valueOf(cArray, 0, n2).intern(); 53 | } 54 | 55 | private String decrypt1(String string, int n) { 56 | n += 5; 57 | char[] cArray = string.toCharArray(); 58 | int n2 = cArray.length; 59 | int n3 = 0; 60 | int n4 = (4 << 1 + 4) - 1 ^ 0x20; 61 | while (n3 != n2) { 62 | int n5 = n3++; 63 | int n6 = cArray[n5] ^ n & n4; 64 | n += 7; 65 | cArray[n5] = (char) n6; 66 | } 67 | return String.valueOf(cArray, 0, n2).intern(); 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/string/SuperBlaubeere27StringTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.string; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.*; 5 | 6 | import javax.crypto.Cipher; 7 | import javax.crypto.spec.SecretKeySpec; 8 | import java.nio.charset.StandardCharsets; 9 | import java.security.MessageDigest; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.Base64; 13 | import java.util.List; 14 | 15 | public class SuperBlaubeere27StringTransformer extends Transformer { 16 | @Override 17 | public void visit(List classMap) { 18 | classMap.forEach(classNode -> { 19 | List methods = new ArrayList<>(); 20 | classNode.methods.forEach(methodNode -> { 21 | AbstractInsnNode[] abstractInsnNodes = methodNode.instructions.toArray(); 22 | for (AbstractInsnNode abstractInsnNode : abstractInsnNodes) { 23 | if (abstractInsnNode.getType() == AbstractInsnNode.LDC_INSN) { 24 | LdcInsnNode ldcInsnNode = (LdcInsnNode) abstractInsnNode; 25 | if (ldcInsnNode.getNext() != null && ldcInsnNode.getNext() instanceof LdcInsnNode) { 26 | LdcInsnNode ldcInsnNodeNext = (LdcInsnNode) ldcInsnNode.getNext(); 27 | if (ldcInsnNodeNext.getNext().getOpcode() == INVOKESTATIC && ldcInsnNodeNext.getNext().getType() == AbstractInsnNode.METHOD_INSN) { 28 | String name = ((MethodInsnNode) ldcInsnNodeNext.getNext()).name; 29 | for (MethodNode mn : classNode.methods) { 30 | if (mn.name.equals(name) && mn.desc.equals(createDescription(DESC_STRING, DESC_STRING, DESC_STRING))) { 31 | ldcInsnNodeNext.cst = decrypt(mn, (String) ldcInsnNode.cst, (String) ldcInsnNodeNext.cst); 32 | methodNode.instructions.remove(ldcInsnNodeNext.getNext()); 33 | methodNode.instructions.remove(ldcInsnNode); 34 | if (!methods.contains(mn)) 35 | methods.add(mn); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | }); 43 | classNode.methods.removeAll(methods); 44 | }); 45 | } 46 | 47 | private String decrypt(MethodNode methodNode, String obj, String key) { 48 | if (hasInstructions(methodNode.instructions, DUP, LDC, INVOKESTATIC, ALOAD, GETSTATIC, INVOKEVIRTUAL, INVOKEVIRTUAL, LDC) && hasStrings(methodNode.instructions, "MD5", "Blowfish")) { 49 | return ll(obj, key); 50 | } 51 | if (hasInstructions(methodNode.instructions, DUP, LDC, INVOKESTATIC, ALOAD, GETSTATIC, INVOKEVIRTUAL, INVOKEVIRTUAL, BIPUSH) && hasStrings(methodNode.instructions, "MD5")) { 52 | return lI(obj, key); 53 | } 54 | if (hasInstructions(methodNode.instructions, DUP, LDC, INVOKESTATIC, ALOAD, LDC, INVOKEVIRTUAL, INVOKEVIRTUAL, LDC) && hasStrings(methodNode.instructions, "SHA-256", "AES")) { 55 | return l(obj, key); 56 | } 57 | if (hasInstructions(methodNode.instructions, DUP, INVOKESTATIC, ALOAD, GETSTATIC, INVOKEVIRTUAL, INVOKEVIRTUAL, GETSTATIC, INVOKESPECIAL)) { 58 | return I(obj, key); 59 | } 60 | return null; 61 | } 62 | 63 | private String l(String obj, String key) { 64 | try { 65 | SecretKeySpec keySpec = new SecretKeySpec(MessageDigest.getInstance("SHA-256").digest(key.getBytes(StandardCharsets.UTF_8)), "AES"); 66 | Cipher des = Cipher.getInstance("AES"); 67 | des.init(2, keySpec); 68 | return new String(des.doFinal(Base64.getDecoder().decode(obj.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8); 69 | } catch (Exception var4) { 70 | return null; 71 | } 72 | } 73 | 74 | private String lI(String obj, String key) { 75 | try { 76 | SecretKeySpec keySpec = new SecretKeySpec(Arrays.copyOf(MessageDigest.getInstance("MD5").digest(key.getBytes(StandardCharsets.UTF_8)), 8), "DES"); 77 | Cipher des = Cipher.getInstance("DES"); 78 | des.init(2, keySpec); 79 | return new String(des.doFinal(Base64.getDecoder().decode(obj.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8); 80 | } catch (Exception var4) { 81 | return null; 82 | } 83 | } 84 | 85 | private String I(String obj, String key) { 86 | obj = new String(Base64.getDecoder().decode(obj.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); 87 | StringBuilder sb = new StringBuilder(); 88 | char[] keyChars = key.toCharArray(); 89 | int i = 0; 90 | char[] var5 = obj.toCharArray(); 91 | 92 | for (char c : var5) { 93 | sb.append((char) (c ^ keyChars[i % keyChars.length])); 94 | ++i; 95 | } 96 | 97 | return sb.toString(); 98 | } 99 | 100 | private String ll(String obj, String key) { 101 | try { 102 | SecretKeySpec keySpec = new SecretKeySpec(MessageDigest.getInstance("MD5").digest(key.getBytes(StandardCharsets.UTF_8)), "Blowfish"); 103 | Cipher des = Cipher.getInstance("Blowfish"); 104 | des.init(2, keySpec); 105 | return new String(des.doFinal(Base64.getDecoder().decode(obj.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8); 106 | } catch (Exception var4) { 107 | return null; 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/transformer/impl/string/qProtectStringTransformer.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.transformer.impl.string; 2 | 3 | import me.ANONIMUS.deobf.transformer.Transformer; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.objectweb.asm.tree.InsnNode; 7 | import org.objectweb.asm.tree.LdcInsnNode; 8 | 9 | import java.util.List; 10 | 11 | public class qProtectStringTransformer extends Transformer { 12 | @Override 13 | public void visit(List classMap) { 14 | classMap.forEach(classNode -> classNode.methods.forEach(methodNode -> { 15 | for (AbstractInsnNode insn : methodNode.instructions.toArray()) { 16 | if (insn instanceof LdcInsnNode && ((LdcInsnNode) insn).cst instanceof String && insn.getPrevious() instanceof InsnNode && insn.getPrevious().getPrevious() instanceof LdcInsnNode) { 17 | methodNode.instructions.remove(insn.getNext()); 18 | methodNode.instructions.set(insn, new LdcInsnNode(decrypt((String) ((LdcInsnNode) insn).cst))); 19 | } 20 | } 21 | })); 22 | } 23 | 24 | private String decrypt(String var0) { 25 | try { 26 | char[] var1 = var0.toCharArray(); 27 | char[] var3 = new char[]{'䠲', '⎅', '⎆', '頓', '鄥', '䖂', 'ओ', '㐢', 'ࡓ', 'ܤ'}; 28 | char[] var10000 = new char[]{'䠠', '萃', '蝓', '㠂', '㡀', '㢔', '蜹', 'း', '茄', '㌳'}; 29 | 30 | for (int i = 0; i < var1.length; i++) { 31 | var1[i] ^= var3[i % 10] ^ var10000[i % 10]; 32 | } 33 | return new String(var1); 34 | } catch (Exception var7) { 35 | return var0; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/me/ANONIMUS/deobf/util/BytecodeUtils.java: -------------------------------------------------------------------------------- 1 | package me.ANONIMUS.deobf.util; 2 | 3 | import org.objectweb.asm.ClassReader; 4 | import org.objectweb.asm.ClassWriter; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.commons.ClassRemapper; 7 | import org.objectweb.asm.commons.SimpleRemapper; 8 | import org.objectweb.asm.tree.*; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.function.Consumer; 15 | import java.util.function.Predicate; 16 | import java.util.stream.IntStream; 17 | 18 | public class BytecodeUtils implements Opcodes { 19 | public boolean isMainMethod(MethodNode methodNode) { 20 | return methodNode.name.equals("main") && methodNode.desc.equals(createDescription(DESC_VOID, DESC_ARRAY_STRING)) && isPublic(methodNode.access) && isStatic(methodNode.access); 21 | } 22 | 23 | public boolean isInitializer(final MethodNode methodNode) { 24 | return methodNode.name.contains("<") || methodNode.name.contains(">"); 25 | } 26 | 27 | public static boolean checkClassVerify(byte[] bytes) { 28 | return String.format("%X%X%X%X", bytes[0], bytes[1], bytes[2], bytes[3]).equals("CAFEBABE"); 29 | } 30 | 31 | public boolean isMainClass(ClassNode classNode) { 32 | for (MethodNode method : classNode.methods) 33 | if (isMainMethod(method)) 34 | return true; 35 | return false; 36 | } 37 | 38 | public boolean isIntInsn(AbstractInsnNode insn) { 39 | if (insn == null) { 40 | return false; 41 | } 42 | int opcode = insn.getOpcode(); 43 | return ((opcode >= ICONST_M1 && opcode <= ICONST_5) 44 | || opcode == BIPUSH 45 | || opcode == SIPUSH 46 | || (insn instanceof LdcInsnNode 47 | && ((LdcInsnNode) insn).cst instanceof Integer)); 48 | } 49 | 50 | public int getIntNumber(AbstractInsnNode insn) { 51 | final int opcode = insn.getOpcode(); 52 | if (opcode >= ICONST_M1 && opcode <= ICONST_5) 53 | return opcode - 3; 54 | else if (insn instanceof IntInsnNode && insn.getOpcode() != NEWARRAY) 55 | return ((IntInsnNode) insn).operand; 56 | else if (insn instanceof LdcInsnNode && ((LdcInsnNode) insn).cst instanceof Integer) 57 | return (Integer) ((LdcInsnNode) insn).cst; 58 | throw new IllegalStateException("Unexpected instruction"); 59 | } 60 | 61 | public void forEach(InsnList instructions, Class type, Consumer consumer) { 62 | AbstractInsnNode[] array = instructions.toArray(); 63 | for (AbstractInsnNode node : array) { 64 | if (node.getClass() == type) { 65 | consumer.accept((T) node); 66 | } 67 | } 68 | } 69 | 70 | public void forEach(InsnList instructions, Consumer consumer) { 71 | forEach(instructions, AbstractInsnNode.class, consumer); 72 | } 73 | 74 | public void applyMappings(List classMap, Map remap) { 75 | SimpleRemapper remapper = new SimpleRemapper(remap); 76 | for (ClassNode node : new ArrayList<>(classMap)) { 77 | ClassNode copy = new ClassNode(); 78 | ClassRemapper adapter = new ClassRemapper(copy, remapper); 79 | node.accept(adapter); 80 | classMap.remove(node); 81 | classMap.add(copy); 82 | } 83 | } 84 | 85 | 86 | public T findFirst(Collection collection, Predicate predicate) { 87 | for (T t : collection) 88 | if (predicate.test(t)) 89 | return t; 90 | return null; 91 | } 92 | 93 | public MethodNode getMethod(ClassNode node, String name, String desc) { 94 | return findFirst(node.methods, m -> m.name.equals(name) && m.desc.equals(desc)); 95 | } 96 | 97 | public ClassNode getOwner(MethodNode m, List classMap) { 98 | return findFirst(classMap, c -> c.methods.contains(m)); 99 | } 100 | 101 | public ClassNode getOwner(FieldNode f, List classMap) { 102 | return findFirst(classMap, c -> c.fields.contains(f)); 103 | } 104 | 105 | public ClassNode getClassNode(String name, List classMap) { 106 | return findFirst(classMap, c -> c.name.equals(name)); 107 | } 108 | 109 | public final byte DESC_EMPTY = -1; 110 | public final byte DESC_VOID = 0; 111 | public final byte DESC_OBJECT = 1; 112 | public final byte DESC_STRING = 2; 113 | public final byte DESC_INTEGER = 3; 114 | public final byte DESC_CLASS = 4; 115 | public final byte DESC_BOOLEAN_TYPE = 5; 116 | public final byte DESC_INT = 6; 117 | public final byte DESC_LONG = 7; 118 | public final byte DESC_FLOAT = 8; 119 | public final byte DESC_DOUBLE = 9; 120 | public final byte DESC_SHORT = 10; 121 | public final byte DESC_BOOLEAN = 11; 122 | public final byte DESC_CHAR = 12; 123 | public final byte DESC_BYTE = 13; 124 | 125 | public final byte DESC_ARRAY_OBJECT = 101; 126 | public final byte DESC_ARRAY_STRING = 102; 127 | public final byte DESC_ARRAY_INTEGER = 103; 128 | public final byte DESC_ARRAY_CLASS = 104; 129 | public final byte DESC_ARRAY_BOOLEAN_TYPE = 105; 130 | public final byte DESC_ARRAY_INT = 106; 131 | public final byte DESC_ARRAY_LONG = 107; 132 | public final byte DESC_ARRAY_FLOAT = 108; 133 | public final byte DESC_ARRAY_DOUBLE = 109; 134 | public final byte DESC_ARRAY_SHORT = 110; 135 | public final byte DESC_ARRAY_BOOLEAN = 111; 136 | public final byte DESC_ARRAY_CHAR = 112; 137 | public final byte DESC_ARRAY_BYTE = 113; 138 | 139 | public String createDescription(byte returnType, byte... arguments) { 140 | StringBuilder desc = new StringBuilder(); 141 | if (arguments.length > 0) 142 | desc.append("("); 143 | IntStream.range(0, arguments.length).forEach(i -> desc.append(getDescription(arguments[i]))); 144 | if (arguments.length > 0) { 145 | desc.append(")"); 146 | } 147 | return desc.append(getDescription(returnType)).toString(); 148 | } 149 | 150 | private String getDescription(byte type) { 151 | if (type > 100) { 152 | return "[" + getDescription((byte) (type - 100)); 153 | } 154 | switch (type) { 155 | case -1: 156 | return ""; 157 | case 0: 158 | return "V"; 159 | case 2: 160 | return "Ljava/lang/String;"; 161 | case 3: 162 | return "Ljava/lang/Integer;"; 163 | case 4: 164 | return "Ljava/lang/Class;"; 165 | case 5: 166 | return "Ljava/lang/Boolean;"; 167 | case 6: 168 | return "I"; 169 | case 7: 170 | return "J"; 171 | case 8: 172 | return "F"; 173 | case 9: 174 | return "D"; 175 | case 10: 176 | return "S"; 177 | case 11: 178 | return "Z"; 179 | case 12: 180 | return "C"; 181 | case 13: 182 | return "B"; 183 | default: 184 | return "Ljava/lang/Object;"; 185 | } 186 | } 187 | 188 | public boolean hasInstructions(InsnList insnList, int... opcodes) { 189 | AbstractInsnNode[] abstractInsnNodes = insnList.toArray(); 190 | if (opcodes.length > abstractInsnNodes.length) { 191 | return false; 192 | } 193 | 194 | int i = 0; 195 | for (int opcode : opcodes) { 196 | for (AbstractInsnNode ab : abstractInsnNodes) { 197 | if (ab.getOpcode() == opcode) { 198 | i++; 199 | } 200 | } 201 | } 202 | return i >= opcodes.length; 203 | } 204 | 205 | public boolean hasStrings(InsnList insnList, String... strings) { 206 | AbstractInsnNode[] abstractInsnNodes = insnList.toArray(); 207 | if (strings.length > abstractInsnNodes.length) { 208 | return false; 209 | } 210 | 211 | int i = 0; 212 | for (String s : strings) { 213 | for (AbstractInsnNode ab : insnList.toArray()) { 214 | if (ab.getType() == AbstractInsnNode.LDC_INSN) { 215 | LdcInsnNode ldcInsnNode = (LdcInsnNode) ab; 216 | if (ldcInsnNode.cst.equals(s)) { 217 | i++; 218 | } 219 | } 220 | } 221 | } 222 | return i >= strings.length; 223 | } 224 | 225 | public static byte[] toByteArray(ClassNode classNode) { 226 | ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 227 | try { 228 | classNode.accept(writer); 229 | return writer.toByteArray(); 230 | } catch (Throwable t) { 231 | writer = new ClassWriter(0); 232 | classNode.accept(writer); 233 | return writer.toByteArray(); 234 | } 235 | } 236 | 237 | public int computeConstantPoolSize(ClassNode classNode) { 238 | return new ClassReader(toByteArray(classNode)).getItemCount(); 239 | } 240 | 241 | public int addAccess(int access, int... add) { 242 | for (int a : add) { 243 | if ((access & a) == 0) { 244 | access |= a; 245 | } 246 | } 247 | return access; 248 | } 249 | 250 | public int removeAccess(int access, int... remove) { 251 | for (int r : remove) { 252 | if ((access & r) != 0) { 253 | access &= ~r; 254 | } 255 | } 256 | return access; 257 | } 258 | 259 | public int setAccess(int... add) { 260 | int access = 0; 261 | for (int a : add) { 262 | access |= a; 263 | } 264 | return access; 265 | } 266 | 267 | public boolean hasAccess(int access, int... check) { 268 | for (int a : check) { 269 | if ((access & a) == 0) { 270 | return false; 271 | } 272 | } 273 | return true; 274 | } 275 | 276 | public boolean isPublic(int access) { 277 | return (access & ACC_PUBLIC) != 0; 278 | } 279 | 280 | public boolean isProtected(int access) { 281 | return (access & ACC_PROTECTED) != 0; 282 | } 283 | 284 | public boolean isPrivate(int access) { 285 | return (access & ACC_PRIVATE) != 0; 286 | } 287 | 288 | public boolean isStatic(int access) { 289 | return (access & ACC_STATIC) != 0; 290 | } 291 | 292 | public boolean isStaticPhase(int access) { 293 | return (access & ACC_STATIC_PHASE) != 0; 294 | } 295 | 296 | public boolean isStrict(int access) { 297 | return (access & ACC_STRICT) != 0; 298 | } 299 | 300 | public boolean isSuper(int access) { 301 | return (access & ACC_SUPER) != 0; 302 | } 303 | 304 | public boolean isNative(int access) { 305 | return (access & ACC_NATIVE) != 0; 306 | } 307 | 308 | public boolean isMandated(int access) { 309 | return (access & ACC_MANDATED) != 0; 310 | } 311 | 312 | public boolean isModule(int access) { 313 | return (access & ACC_MODULE) != 0; 314 | } 315 | 316 | public boolean isOpen(int access) { 317 | return (access & ACC_OPEN) != 0; 318 | } 319 | 320 | public boolean isAbstract(int access) { 321 | return (access & ACC_ABSTRACT) != 0; 322 | } 323 | 324 | public boolean isFinal(int access) { 325 | return (access & ACC_FINAL) != 0; 326 | } 327 | 328 | public boolean isSynthetic(int access) { 329 | return (access & ACC_SYNTHETIC) != 0; 330 | } 331 | 332 | public boolean isTransient(int access) { 333 | return (access & ACC_TRANSIENT) != 0; 334 | } 335 | 336 | public boolean isTransitive(int access) { 337 | return (access & ACC_TRANSITIVE) != 0; 338 | } 339 | 340 | public boolean isVolatile(int access) { 341 | return (access & ACC_VOLATILE) != 0; 342 | } 343 | 344 | public boolean isVarargs(int access) { 345 | return (access & ACC_VARARGS) != 0; 346 | } 347 | 348 | public boolean isBridge(int access) { 349 | return (access & ACC_BRIDGE) != 0; 350 | } 351 | 352 | public boolean isSynchronized(int access) { 353 | return (access & ACC_SYNCHRONIZED) != 0; 354 | } 355 | 356 | public boolean isInterface(int access) { 357 | return (access & ACC_INTERFACE) != 0; 358 | } 359 | 360 | public boolean isEnum(int access) { 361 | return (access & ACC_ENUM) != 0; 362 | } 363 | 364 | public boolean isAnnotation(int access) { 365 | return (access & ACC_ANNOTATION) != 0; 366 | } 367 | 368 | public boolean isDeprecated(int access) { 369 | return (access & ACC_DEPRECATED) != 0; 370 | } 371 | } --------------------------------------------------------------------------------