├── .gitignore ├── LICENSE ├── src └── main │ └── java │ └── com │ └── cheatbreaker │ └── obf │ ├── transformer │ ├── ShuffleTransformer.java │ ├── Transformer.java │ ├── JunkFieldTransformer.java │ ├── AccessTransformer.java │ ├── StringTransformer.java │ └── ConstantTransformer.java │ ├── utils │ ├── RandomUtils.java │ ├── StreamUtils.java │ └── AsmUtils.java │ ├── Main.java │ └── Obf.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | MC.jar 3 | MC.obf.jar 4 | *.iml 5 | target/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 CheatBreaker, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/ShuffleTransformer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.transformer; 26 | 27 | import com.cheatbreaker.obf.Obf; 28 | import org.objectweb.asm.tree.ClassNode; 29 | 30 | import java.util.Collections; 31 | 32 | public class ShuffleTransformer extends Transformer { 33 | 34 | public ShuffleTransformer(Obf obf) { 35 | super(obf); 36 | } 37 | 38 | @Override 39 | public void visit(ClassNode classNode) { 40 | Collections.shuffle(classNode.fields, random); 41 | Collections.shuffle(classNode.methods, random); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/Transformer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.transformer; 26 | 27 | import com.cheatbreaker.obf.Obf; 28 | import org.objectweb.asm.tree.ClassNode; 29 | 30 | import java.util.Random; 31 | 32 | public abstract class Transformer { 33 | 34 | protected final Obf obf; 35 | protected final Random random; 36 | 37 | public Transformer(Obf obf) { 38 | this.obf = obf; 39 | this.random = obf.getRandom(); 40 | } 41 | 42 | public abstract void visit(ClassNode classNode); 43 | 44 | public void after() { 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/RandomUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.utils; 26 | 27 | import java.util.Arrays; 28 | import java.util.List; 29 | import java.util.Random; 30 | 31 | public class RandomUtils { 32 | public static List swap(Random random, T a, T b) { 33 | if (random.nextBoolean()) { 34 | return Arrays.asList(a, b); 35 | } else { 36 | return Arrays.asList(b, a); 37 | } 38 | } 39 | 40 | public static T choice(Random random, List items) { 41 | return items.get(random.nextInt(items.size())); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/StreamUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.utils; 26 | 27 | import java.io.ByteArrayOutputStream; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.io.OutputStream; 31 | 32 | public class StreamUtils { 33 | 34 | public static void copy(InputStream in, OutputStream out) throws IOException { 35 | int read; 36 | byte[] buffer = new byte[0x1000]; 37 | while ((read = in.read(buffer)) != -1) { 38 | out.write(buffer, 0, read); 39 | } 40 | } 41 | 42 | public static byte[] readAll(InputStream in) throws IOException { 43 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 44 | byte[] buffer = new byte[0x1000]; 45 | int read; 46 | while((read = in.read(buffer)) != -1) { 47 | out.write(buffer, 0, read); 48 | } 49 | return out.toByteArray(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/JunkFieldTransformer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.transformer; 26 | 27 | import com.cheatbreaker.obf.Obf; 28 | import com.cheatbreaker.obf.utils.RandomUtils; 29 | import org.objectweb.asm.Opcodes; 30 | import org.objectweb.asm.tree.ClassNode; 31 | import org.objectweb.asm.tree.FieldNode; 32 | 33 | public class JunkFieldTransformer extends Transformer { 34 | public JunkFieldTransformer(Obf obf) { 35 | super(obf); 36 | } 37 | 38 | @Override 39 | public void visit(ClassNode classNode) { 40 | if ((classNode.access & Opcodes.ACC_INTERFACE) != 0) { 41 | return; 42 | } 43 | 44 | // add fields of this class type to a few random classes 45 | for (int i = random.nextInt(3) + 1; i >= 0; i--) { 46 | ClassNode target = RandomUtils.choice(random, obf.getClasses()); 47 | if ((target.access & Opcodes.ACC_INTERFACE) != 0) { 48 | continue; 49 | } 50 | String name = "__junk" + Math.abs(random.nextLong()); 51 | target.fields.add(new FieldNode(Opcodes.ACC_PUBLIC, name, "L" + classNode.name + ";", null, null)); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/Main.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf; 26 | 27 | import joptsimple.OptionException; 28 | import joptsimple.OptionParser; 29 | import joptsimple.OptionSet; 30 | 31 | import java.io.File; 32 | import java.io.IOException; 33 | 34 | public class Main { 35 | public static void main(String[] args) { 36 | OptionParser parser = new OptionParser(); 37 | parser.accepts("input").withRequiredArg().required().ofType(File.class); 38 | parser.accepts("output").withRequiredArg().required().ofType(File.class); 39 | 40 | OptionSet options; 41 | 42 | try { 43 | options = parser.parse(args); 44 | } catch (OptionException ex) { 45 | System.out.println("Usage: obf --input --output "); 46 | System.out.println(ex.getMessage()); 47 | System.exit(1); 48 | return; 49 | } 50 | 51 | File inputFile = (File) options.valueOf("input"); 52 | File outputFile = (File) options.valueOf("output"); 53 | 54 | try { 55 | new Obf(inputFile, outputFile); 56 | } catch (IOException ex) { 57 | ex.printStackTrace(); 58 | System.exit(1); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/AccessTransformer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.transformer; 26 | 27 | import com.cheatbreaker.obf.Obf; 28 | import org.objectweb.asm.Opcodes; 29 | import org.objectweb.asm.tree.ClassNode; 30 | import org.objectweb.asm.tree.FieldNode; 31 | import org.objectweb.asm.tree.MethodNode; 32 | 33 | public class AccessTransformer extends Transformer { 34 | public AccessTransformer(Obf obf) { 35 | super(obf); 36 | } 37 | 38 | @Override 39 | public void visit(ClassNode classNode) { 40 | classNode.access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_FINAL); 41 | classNode.access |= Opcodes.ACC_PUBLIC; 42 | if ((classNode.access & Opcodes.ACC_INTERFACE) == 0) { 43 | for (FieldNode field : classNode.fields) { 44 | field.access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_FINAL); 45 | field.access |= Opcodes.ACC_PUBLIC; 46 | } 47 | for (MethodNode method : classNode.methods) { 48 | method.access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_FINAL); 49 | method.access |= Opcodes.ACC_PUBLIC; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.cheatbreaker 8 | obf 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | net.sf.jopt-simple 19 | jopt-simple 20 | 5.0.2 21 | 22 | 23 | org.ow2.asm 24 | asm-debug-all 25 | 5.0.3 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-jar-plugin 33 | 3.0.2 34 | 35 | 36 | 37 | true 38 | lib/ 39 | com.cheatbreaker.obf.Main 40 | 41 | 42 | 43 | 44 | 45 | org.apache.maven.plugins 46 | maven-compiler-plugin 47 | 48 | 1.8 49 | 1.8 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-shade-plugin 55 | 2.4.3 56 | 57 | 58 | package 59 | 60 | shade 61 | 62 | 63 | 64 | 65 | net.sf.jopt-simple:jopt-simple 66 | org.ow2.asm:asm-debug-all 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/utils/AsmUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.utils; 26 | 27 | import org.objectweb.asm.Opcodes; 28 | import org.objectweb.asm.commons.CodeSizeEvaluator; 29 | import org.objectweb.asm.tree.AbstractInsnNode; 30 | import org.objectweb.asm.tree.InsnNode; 31 | import org.objectweb.asm.tree.IntInsnNode; 32 | import org.objectweb.asm.tree.LdcInsnNode; 33 | import org.objectweb.asm.tree.MethodNode; 34 | 35 | public class AsmUtils { 36 | public static boolean isPushInt(AbstractInsnNode insn) { 37 | int op = insn.getOpcode(); 38 | return (op >= Opcodes.ICONST_M1 && op <= Opcodes.ICONST_5) 39 | || op == Opcodes.BIPUSH 40 | || op == Opcodes.SIPUSH 41 | || (op == Opcodes.LDC && ((LdcInsnNode) insn).cst instanceof Integer); 42 | } 43 | 44 | public static int getPushedInt(AbstractInsnNode insn) { 45 | int op = insn.getOpcode(); 46 | if (op >= Opcodes.ICONST_M1 && op <= Opcodes.ICONST_5) { 47 | return op - Opcodes.ICONST_0; 48 | } 49 | if (op == Opcodes.BIPUSH || op == Opcodes.SIPUSH) { 50 | return ((IntInsnNode) insn).operand; 51 | } 52 | if (op == Opcodes.LDC) { 53 | Object cst = ((LdcInsnNode) insn).cst; 54 | if (cst instanceof Integer) { 55 | return (int) cst; 56 | } 57 | } 58 | throw new IllegalArgumentException("insn is not a push int instruction"); 59 | } 60 | 61 | public static AbstractInsnNode pushInt(int value) { 62 | if (value >= -1 && value <= 5) { 63 | return new InsnNode(Opcodes.ICONST_0 + value); 64 | } 65 | if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { 66 | return new IntInsnNode(Opcodes.BIPUSH, value); 67 | } 68 | if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { 69 | return new IntInsnNode(Opcodes.SIPUSH, value); 70 | } 71 | return new LdcInsnNode(value); 72 | } 73 | 74 | public static boolean isPushLong(AbstractInsnNode insn) { 75 | int op = insn.getOpcode(); 76 | return op == Opcodes.LCONST_0 77 | || op == Opcodes.LCONST_1 78 | || (op == Opcodes.LDC && ((LdcInsnNode) insn).cst instanceof Long); 79 | } 80 | 81 | public static long getPushedLong(AbstractInsnNode insn) { 82 | int op = insn.getOpcode(); 83 | if (op == Opcodes.LCONST_0) { 84 | return 0; 85 | } 86 | if (op == Opcodes.LCONST_1) { 87 | return 1; 88 | } 89 | if (op == Opcodes.LDC) { 90 | Object cst = ((LdcInsnNode) insn).cst; 91 | if (cst instanceof Long) { 92 | return (long) cst; 93 | } 94 | } 95 | throw new IllegalArgumentException("insn is not a push long instruction"); 96 | } 97 | 98 | public static AbstractInsnNode pushLong(long value) { 99 | if (value == 0) { 100 | return new InsnNode(Opcodes.LCONST_0); 101 | } 102 | if (value == 1) { 103 | return new InsnNode(Opcodes.LCONST_1); 104 | } 105 | return new LdcInsnNode(value); 106 | } 107 | 108 | public static int codeSize(MethodNode methodNode) { 109 | CodeSizeEvaluator evaluator = new CodeSizeEvaluator(null); 110 | methodNode.accept(evaluator); 111 | return evaluator.getMaxSize(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/Obf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf; 26 | 27 | import com.cheatbreaker.obf.transformer.AccessTransformer; 28 | import com.cheatbreaker.obf.transformer.ConstantTransformer; 29 | import com.cheatbreaker.obf.transformer.JunkFieldTransformer; 30 | import com.cheatbreaker.obf.transformer.ShuffleTransformer; 31 | import com.cheatbreaker.obf.transformer.StringTransformer; 32 | import com.cheatbreaker.obf.transformer.Transformer; 33 | import com.cheatbreaker.obf.utils.StreamUtils; 34 | import org.objectweb.asm.ClassReader; 35 | import org.objectweb.asm.ClassWriter; 36 | import org.objectweb.asm.tree.ClassNode; 37 | 38 | import java.io.File; 39 | import java.io.FileOutputStream; 40 | import java.io.IOException; 41 | import java.io.InputStream; 42 | import java.util.ArrayList; 43 | import java.util.Collections; 44 | import java.util.Enumeration; 45 | import java.util.List; 46 | import java.util.Random; 47 | import java.util.jar.JarEntry; 48 | import java.util.jar.JarFile; 49 | import java.util.jar.JarOutputStream; 50 | 51 | public class Obf { 52 | 53 | private final Random random; 54 | private final List classes = new ArrayList<>(); 55 | private final List transformers = new ArrayList<>(); 56 | private final List newClasses = new ArrayList<>(); 57 | 58 | public Obf(File inputFile, File outputFile) throws IOException { 59 | random = new Random(); 60 | 61 | transformers.add(new ConstantTransformer(this)); 62 | transformers.add(new StringTransformer(this)); 63 | transformers.add(new JunkFieldTransformer(this)); 64 | transformers.add(new AccessTransformer(this)); 65 | transformers.add(new ShuffleTransformer(this)); 66 | 67 | JarFile inputJar = new JarFile(inputFile); 68 | 69 | try (JarOutputStream out = new JarOutputStream(new FileOutputStream(outputFile))) { 70 | 71 | // read all classes into this.classes and copy all resources to output jar 72 | System.out.println("Reading jar..."); 73 | for (Enumeration iter = inputJar.entries(); iter.hasMoreElements(); ) { 74 | JarEntry entry = iter.nextElement(); 75 | try (InputStream in = inputJar.getInputStream(entry)) { 76 | if (entry.getName().endsWith(".class")) { 77 | ClassReader reader = new ClassReader(in); 78 | ClassNode classNode = new ClassNode(); 79 | reader.accept(classNode, 0); 80 | classes.add(classNode); 81 | } else { 82 | out.putNextEntry(new JarEntry(entry.getName())); 83 | StreamUtils.copy(in, out); 84 | } 85 | } 86 | } 87 | 88 | // shuffle the entries in case the order in the output jar gives away information 89 | Collections.shuffle(classes, random); 90 | 91 | System.out.println("Transforming classes..."); 92 | for (Transformer transformer : transformers) { 93 | System.out.println("Running " + transformer.getClass().getSimpleName() + "..."); 94 | classes.forEach(transformer::visit); 95 | } 96 | for (Transformer transformer : transformers) { 97 | transformer.after(); 98 | } 99 | 100 | System.out.println("Writing classes..."); 101 | for (ClassNode classNode : classes) { 102 | ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); 103 | classNode.accept(writer); 104 | out.putNextEntry(new JarEntry(classNode.name + ".class")); 105 | out.write(writer.toByteArray()); 106 | } 107 | 108 | System.out.println("Writing generated classes..."); 109 | for (ClassNode classNode : newClasses) { 110 | ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 111 | classNode.accept(writer); 112 | out.putNextEntry(new JarEntry(classNode.name + ".class")); 113 | out.write(writer.toByteArray()); 114 | } 115 | } 116 | } 117 | 118 | public Random getRandom() { 119 | return random; 120 | } 121 | 122 | public List getClasses() { 123 | return classes; 124 | } 125 | 126 | public void addNewClass(ClassNode classNode) { 127 | newClasses.add(classNode); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/StringTransformer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.transformer; 26 | 27 | import com.cheatbreaker.obf.Obf; 28 | import com.cheatbreaker.obf.utils.AsmUtils; 29 | import org.objectweb.asm.Opcodes; 30 | import org.objectweb.asm.tree.AbstractInsnNode; 31 | import org.objectweb.asm.tree.ClassNode; 32 | import org.objectweb.asm.tree.FieldInsnNode; 33 | import org.objectweb.asm.tree.FieldNode; 34 | import org.objectweb.asm.tree.InsnNode; 35 | import org.objectweb.asm.tree.LdcInsnNode; 36 | import org.objectweb.asm.tree.MethodNode; 37 | import org.objectweb.asm.tree.TypeInsnNode; 38 | 39 | import java.util.ArrayList; 40 | import java.util.Iterator; 41 | import java.util.List; 42 | 43 | public class StringTransformer extends Transformer { 44 | 45 | private static final int PARTITION_BITS = 10; 46 | private static final int PARTITION_SIZE = 1 << PARTITION_BITS; 47 | private static final int PARTITION_MASK = PARTITION_SIZE - 1; 48 | private List strings = new ArrayList<>(); 49 | 50 | public StringTransformer(Obf obf) { 51 | super(obf); 52 | } 53 | 54 | @Override 55 | public void visit(ClassNode classNode) { 56 | for (MethodNode method : classNode.methods) { 57 | for (Iterator iter = method.instructions.iterator(); iter.hasNext(); ) { 58 | AbstractInsnNode insn = iter.next(); 59 | if (insn.getOpcode() == Opcodes.LDC) { 60 | LdcInsnNode ldc = (LdcInsnNode) insn; 61 | if (ldc.cst instanceof String) { 62 | String string = (String) ldc.cst; 63 | int id = strings.indexOf(string); 64 | if (id == -1) { 65 | id = strings.size(); 66 | strings.add(string); 67 | } 68 | int index = id & PARTITION_MASK; 69 | int classId = id >> PARTITION_BITS; 70 | int mask = (short) random.nextInt(); 71 | int a = (short) random.nextInt() & mask | index; 72 | int b = (short) random.nextInt() & ~mask | index; 73 | method.instructions.insertBefore(insn, new FieldInsnNode(Opcodes.GETSTATIC, "generated/Strings" + classId, "strings", "[Ljava/lang/String;")); 74 | method.instructions.insertBefore(insn, AsmUtils.pushInt(a)); 75 | method.instructions.insertBefore(insn, AsmUtils.pushInt(b)); 76 | method.instructions.insertBefore(insn, new InsnNode(Opcodes.IAND)); 77 | method.instructions.insertBefore(insn, new InsnNode(Opcodes.AALOAD)); 78 | iter.remove(); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | @Override 86 | public void after() { 87 | for (int classId = 0; classId <= strings.size() >> PARTITION_BITS; classId++) { 88 | ClassNode classNode = new ClassNode(); 89 | classNode.version = Opcodes.V1_8; 90 | classNode.access = Opcodes.ACC_PUBLIC; 91 | classNode.name = "generated/Strings" + classId; 92 | classNode.superName = "java/lang/Object"; 93 | classNode.fields.add(new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "strings", "[Ljava/lang/String;", null, null)); 94 | MethodNode clinit = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "", "()V", null, null); 95 | classNode.methods.add(clinit); 96 | int start = classId << PARTITION_BITS; 97 | int end = Math.min(start + PARTITION_SIZE, strings.size()); 98 | clinit.instructions.add(AsmUtils.pushInt(end - start)); 99 | clinit.instructions.add(new TypeInsnNode(Opcodes.ANEWARRAY, "Ljava/lang/String;")); 100 | for (int id = start; id < end; id++) { 101 | clinit.instructions.add(new InsnNode(Opcodes.DUP)); 102 | clinit.instructions.add(AsmUtils.pushInt(id & PARTITION_MASK)); 103 | clinit.instructions.add(new LdcInsnNode(strings.get(id))); 104 | clinit.instructions.add(new InsnNode(Opcodes.AASTORE)); 105 | } 106 | clinit.instructions.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, "strings", "[Ljava/lang/String;")); 107 | clinit.instructions.add(new InsnNode(Opcodes.RETURN)); 108 | obf.addNewClass(classNode); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/cheatbreaker/obf/transformer/ConstantTransformer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2018 CheatBreaker, LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.cheatbreaker.obf.transformer; 26 | 27 | import com.cheatbreaker.obf.Obf; 28 | import com.cheatbreaker.obf.utils.AsmUtils; 29 | import com.cheatbreaker.obf.utils.RandomUtils; 30 | import org.objectweb.asm.Opcodes; 31 | import org.objectweb.asm.tree.AbstractInsnNode; 32 | import org.objectweb.asm.tree.ClassNode; 33 | import org.objectweb.asm.tree.InsnList; 34 | import org.objectweb.asm.tree.InsnNode; 35 | import org.objectweb.asm.tree.LdcInsnNode; 36 | import org.objectweb.asm.tree.MethodNode; 37 | 38 | import java.util.Iterator; 39 | 40 | public class ConstantTransformer extends Transformer { 41 | 42 | public ConstantTransformer(Obf obf) { 43 | super(obf); 44 | } 45 | 46 | @Override 47 | public void visit(ClassNode classNode) { 48 | for (MethodNode method : classNode.methods) { 49 | if (AsmUtils.codeSize(method) > 0x4000) { 50 | System.out.println(classNode.name + "." + method.name + method.desc + " is too large, skipping"); 51 | continue; 52 | } 53 | for (Iterator iter = method.instructions.iterator(); iter.hasNext(); ) { 54 | AbstractInsnNode insn = iter.next(); 55 | if (AsmUtils.isPushInt(insn)) { 56 | int value = AsmUtils.getPushedInt(insn); 57 | method.instructions.insertBefore(insn, makeInt(value)); 58 | iter.remove(); 59 | } else if (AsmUtils.isPushLong(insn)) { 60 | long value = AsmUtils.getPushedLong(insn); 61 | method.instructions.insertBefore(insn, makeLong(value)); 62 | iter.remove(); 63 | } else if (insn.getOpcode() == Opcodes.LDC) { 64 | LdcInsnNode ldc = (LdcInsnNode) insn; 65 | if (ldc.cst instanceof Float) { 66 | method.instructions.insertBefore(insn, makeFloat((float) ldc.cst)); 67 | iter.remove(); 68 | } else if (ldc.cst instanceof Double) { 69 | method.instructions.insertBefore(insn, makeDouble((double) ldc.cst)); 70 | iter.remove(); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | private InsnList makeInt(int value) { 78 | InsnList instructions = new InsnList(); 79 | int mask = random.nextInt(); 80 | int a = random.nextInt() & mask | value; 81 | int b = random.nextInt() & ~mask | value; 82 | if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { 83 | // try to use bipush/sipush for lower values 84 | a = (short) a; 85 | b = (short) b; 86 | } 87 | instructions.add(AsmUtils.pushInt(a)); 88 | instructions.add(AsmUtils.pushInt(b)); 89 | instructions.add(new InsnNode(Opcodes.IAND)); 90 | return instructions; 91 | } 92 | 93 | private InsnList makeLong(long value) { 94 | InsnList instructions = new InsnList(); 95 | long mask = random.nextLong(); 96 | long a = random.nextInt() & mask | value; 97 | long b = random.nextInt() & ~mask | value; 98 | instructions.add(AsmUtils.pushLong(a)); 99 | instructions.add(AsmUtils.pushLong(b)); 100 | instructions.add(new InsnNode(Opcodes.LAND)); 101 | return instructions; 102 | } 103 | 104 | private InsnList makeFloat(float value) { 105 | InsnList instructions = new InsnList(); 106 | for (int retry = 0; retry < 10; retry++) { 107 | float multiplier = (float) (random.nextInt(99) + 1) / (float) (random.nextInt(99) + 1); 108 | if (value / multiplier * multiplier == value) { 109 | RandomUtils.swap(random, new LdcInsnNode(value / multiplier), new LdcInsnNode(multiplier)).forEach(instructions::add); 110 | instructions.add(new InsnNode(Opcodes.FMUL)); 111 | return instructions; 112 | } 113 | } 114 | instructions.add(new LdcInsnNode(value)); 115 | return instructions; 116 | } 117 | 118 | private InsnList makeDouble(double value) { 119 | InsnList instructions = new InsnList(); 120 | for (int retry = 0; retry < 10; retry++) { 121 | double multiplier = (double) (random.nextInt(99) + 1) / (double) (random.nextInt(99) + 1); 122 | if (value / multiplier * multiplier == value) { 123 | RandomUtils.swap(random, new LdcInsnNode(value / multiplier), new LdcInsnNode(multiplier)).forEach(instructions::add); 124 | instructions.add(new InsnNode(Opcodes.DMUL)); 125 | return instructions; 126 | } 127 | } 128 | instructions.add(new LdcInsnNode(value)); 129 | return instructions; 130 | } 131 | } 132 | --------------------------------------------------------------------------------