diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5d4318 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Ignore everything +* +!*/ + +# Expect sources and libs +!*.java +!*.jar +!MANIFEST.MF + +# Except Git +!.gitignore +!*.md + +# Expect Makefile +!Makefile diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c0623c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Abstractor + +Try to steal my Minecraft server's API now. 😘 diff --git a/lib/asm-all-5.2.jar b/lib/asm-all-5.2.jar new file mode 100644 index 0000000..0b629c2 Binary files /dev/null and b/lib/asm-all-5.2.jar differ diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5f8851e --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: com.alphabet.abstractor.Main + diff --git a/src/com/alphabet/abstractor/Main.java b/src/com/alphabet/abstractor/Main.java new file mode 100644 index 0000000..63b0451 --- /dev/null +++ b/src/com/alphabet/abstractor/Main.java @@ -0,0 +1,23 @@ +package com.alphabet.abstractor; + +import com.alphabet.abstractor.utils.Loader; +import com.alphabet.abstractor.utils.MethodModifier; +import com.alphabet.abstractor.utils.Saver; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +public class Main { + + public static void main(String[] args) throws IOException { + if (args.length < 1) + System.out.println("java -jar abstractor.jar [jar]"); + + else { + Map classesMap = Loader.loadClasses(new File(args[0])); + Map bytesMap = Saver.processNodes(classesMap); + Saver.saveAsJar(bytesMap, "output.jar"); + } + } +} diff --git a/src/com/alphabet/abstractor/utils/Loader.java b/src/com/alphabet/abstractor/utils/Loader.java new file mode 100644 index 0000000..b1f45e3 --- /dev/null +++ b/src/com/alphabet/abstractor/utils/Loader.java @@ -0,0 +1,60 @@ +package com.alphabet.abstractor.utils; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Stream; + +public class Loader { + + public static Map loadClasses(File jarFile) throws IOException { + Map classes = new HashMap<>(); + + try (JarFile jar = new JarFile(jarFile); Stream str = jar.stream()) { + str.forEach(entry -> readJar(jar, entry, classes)); + } + + return classes; + } + + private static Map readJar(JarFile jar, JarEntry entry, Map classes) { + String name = entry.getName(); + + try (InputStream is = jar.getInputStream(entry)) { + if (name.endsWith(".class")) { + try { + MethodModifier methodReplacer = getReplacer(is); + + if (methodReplacer.shouldIgnore) + return classes; + + classes.put(name, methodReplacer); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + } catch (IOException e) { + e.printStackTrace(); + } + + return classes; + } + + private static MethodModifier getReplacer(InputStream stream) throws IOException { + ClassReader classReader = new ClassReader(stream); + ClassWriter classWriter = new ClassWriter(0); + MethodModifier methodReplacer = new MethodModifier(classWriter); + + classReader.accept(methodReplacer, 0); + return methodReplacer; + } +} diff --git a/src/com/alphabet/abstractor/utils/MethodModifier.java b/src/com/alphabet/abstractor/utils/MethodModifier.java new file mode 100644 index 0000000..cc1b544 --- /dev/null +++ b/src/com/alphabet/abstractor/utils/MethodModifier.java @@ -0,0 +1,42 @@ +package com.alphabet.abstractor.utils; + +import org.objectweb.asm.*; + +public class MethodModifier extends ClassVisitor { + + public boolean shouldIgnore; + + MethodModifier(ClassWriter writer) { + super(Opcodes.ASM5, writer); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if ((access & Opcodes.ACC_PUBLIC) == 0) + shouldIgnore = true; + else + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if ((access & Opcodes.ACC_PUBLIC) == 0) + return null; + + return new ReplacerMethod( + Type.getReturnType(desc), + super.visitMethod(access, name, desc, signature, exceptions)); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + if ((access & Opcodes.ACC_PUBLIC) == 0) + return null; + + return super.visitField(access, name, desc, signature, value); + } + + public byte[] toByteArray() { + return ((ClassWriter) this.cv).toByteArray(); + } +} \ No newline at end of file diff --git a/src/com/alphabet/abstractor/utils/ReplacerMethod.java b/src/com/alphabet/abstractor/utils/ReplacerMethod.java new file mode 100644 index 0000000..b1c76e8 --- /dev/null +++ b/src/com/alphabet/abstractor/utils/ReplacerMethod.java @@ -0,0 +1,30 @@ +package com.alphabet.abstractor.utils; + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +public class ReplacerMethod extends MethodVisitor { + + private final Type returnType; + private final MethodVisitor targetWriter; + + ReplacerMethod(Type returnType, MethodVisitor writer) { + super(Opcodes.ASM5); + this.returnType = returnType; + this.targetWriter = writer; + } + + @Override + public void visitCode() { + targetWriter.visitCode(); + + if (returnType == Type.VOID_TYPE) + targetWriter.visitInsn(Opcodes.RETURN); + + else { + targetWriter.visitInsn(Opcodes.ACONST_NULL); + targetWriter.visitInsn(Opcodes.ARETURN); + } + } +} diff --git a/src/com/alphabet/abstractor/utils/Saver.java b/src/com/alphabet/abstractor/utils/Saver.java new file mode 100644 index 0000000..b029192 --- /dev/null +++ b/src/com/alphabet/abstractor/utils/Saver.java @@ -0,0 +1,33 @@ +package com.alphabet.abstractor.utils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; + +public class Saver { + + public static Map processNodes(Map replacerMap) { + Map out = new HashMap<>(); + + for (Map.Entry entry : replacerMap.entrySet()) + out.put(entry.getKey(), entry.getValue().toByteArray()); + + return out; + } + + public static void saveAsJar(Map outBytes, String fileName) { + try (JarOutputStream out = new JarOutputStream(new FileOutputStream(fileName))) { + for (String entry : outBytes.keySet()) { + out.putNextEntry(new ZipEntry(entry)); + out.write(outBytes.get(entry)); + out.closeEntry(); + } + + } catch (IOException e) { + e.printStackTrace(); + } + } +}