├── README.md ├── src └── main │ └── java │ └── net │ └── inzynierr │ └── malware_remover │ ├── MalwareClassVisitor.java │ ├── MalwareMethodVisitor.java │ └── MalwareRemoverApplication.java └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | # spigot-malware-remover 2 | Simple spigot plugin malware remover made in 10 minutes, using ASM bytecode manipulation library.
3 | Compile it using maven, if you can. 4 | > JDK version used by this project: Adoptium Temurin 17 5 | 6 | ## Usage: 7 | * java -jar 8 | -------------------------------------------------------------------------------- /src/main/java/net/inzynierr/malware_remover/MalwareClassVisitor.java: -------------------------------------------------------------------------------- 1 | package net.inzynierr.malware_remover; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.ClassWriter; 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | 8 | public class MalwareClassVisitor extends ClassVisitor { 9 | 10 | private boolean canModify; 11 | 12 | public MalwareClassVisitor(ClassWriter writer) { 13 | super(Opcodes.ASM9, writer); 14 | } 15 | 16 | @Override 17 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, 18 | String[] exceptions) { 19 | return name.equals("onEnable") && this.canModify ? new MalwareMethodVisitor( 20 | super.visitMethod(access, name, descriptor, signature, exceptions)) 21 | : super.visitMethod(access, name, descriptor, signature, exceptions); 22 | } 23 | 24 | @Override 25 | public void visit(int version, int access, String name, String signature, String superName, 26 | String[] interfaces) { 27 | this.canModify = superName != null && superName.equals("org/bukkit/plugin/java/JavaPlugin"); 28 | super.visit(version, access, name, signature, superName, interfaces); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/inzynierr/malware_remover/MalwareMethodVisitor.java: -------------------------------------------------------------------------------- 1 | package net.inzynierr.malware_remover; 2 | 3 | import org.objectweb.asm.MethodVisitor; 4 | import org.objectweb.asm.Opcodes; 5 | 6 | public class MalwareMethodVisitor extends MethodVisitor { 7 | 8 | private boolean canIgnore; 9 | 10 | public MalwareMethodVisitor(MethodVisitor visitor) { 11 | super(Opcodes.ASM9, visitor); 12 | } 13 | 14 | @Override 15 | public void visitMethodInsn(int opcode, String owner, String name, String descriptor, 16 | boolean isInterface) { 17 | if (this.canIgnore) { 18 | if (opcode == Opcodes.INVOKEVIRTUAL && MalwareRemoverApplication.BLACKLISTED_FILES 19 | .matcher(owner).find() && name.equals("a")) { 20 | this.canIgnore = false; 21 | } 22 | 23 | return; 24 | } 25 | 26 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); 27 | } 28 | 29 | @Override 30 | public void visitTypeInsn(int opcode, String type) { 31 | if (opcode == Opcodes.NEW && MalwareRemoverApplication.BLACKLISTED_FILES.matcher(type).find()) { 32 | this.canIgnore = true; 33 | return; 34 | } 35 | 36 | super.visitTypeInsn(opcode, type); 37 | } 38 | 39 | @Override 40 | public void visitVarInsn(int opcode, int var) { 41 | if (!this.canIgnore) { 42 | super.visitVarInsn(opcode, var); 43 | } 44 | } 45 | 46 | @Override 47 | public void visitInsn(int opcode) { 48 | if (!this.canIgnore) { 49 | super.visitInsn(opcode); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | net.inzynierr.malware_remover 8 | trash-malware-remover 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 17 13 | 17 14 | 15 | 16 | 17 | 18 | org.ow2.asm 19 | asm 20 | 9.3 21 | 22 | 23 | 24 | org.ow2.asm 25 | asm-tree 26 | 9.3 27 | 28 | 29 | 30 | 31 | clean install 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-shade-plugin 36 | 3.3.0 37 | 38 | 39 | package 40 | 41 | shade 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | net.inzynierr.malware_remover.MalwareRemoverApplication 50 | 51 | 52 | 53 | false 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/java/net/inzynierr/malware_remover/MalwareRemoverApplication.java: -------------------------------------------------------------------------------- 1 | package net.inzynierr.malware_remover; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.jar.JarEntry; 12 | import java.util.jar.JarFile; 13 | import java.util.jar.JarOutputStream; 14 | import java.util.regex.Pattern; 15 | import java.util.stream.Collectors; 16 | import org.objectweb.asm.ClassReader; 17 | import org.objectweb.asm.ClassWriter; 18 | 19 | public final class MalwareRemoverApplication { 20 | 21 | static final Pattern BLACKLISTED_FILES = Pattern.compile("(javassist|.l(\\d+|_ignore)|L\\d+)"); 22 | 23 | public static void main(String[] args) throws IOException { 24 | if (args.length != 1) { 25 | System.err.println("Usage: trash-malware-remover.jar "); 26 | return; 27 | } 28 | 29 | var input = Paths.get(args[0]); 30 | if (!Files.exists(input) || !Files.isDirectory(input)) { 31 | System.err.println("The input directory doesn't even exists."); 32 | return; 33 | } 34 | 35 | for (var file : scanJarFiles(input)) { 36 | System.out.printf("Processing %s%n", file.getAbsolutePath()); 37 | patchJarFile(file); 38 | } 39 | } 40 | 41 | private static List scanJarFiles(Path input) throws IOException { 42 | try (var stream = Files.walk(input)) { 43 | return stream.filter( 44 | path -> Files.isReadable(path) && Files.isExecutable(path) && path.getFileName() 45 | .toString().endsWith(".jar")).map(Path::toFile).collect(Collectors.toList()); 46 | } 47 | } 48 | 49 | private static void patchJarFile(File file) throws IOException { 50 | var written = new HashMap(); 51 | try (var jarFile = new JarFile(file)) { 52 | var enumeration = jarFile.entries(); 53 | while (enumeration.hasMoreElements()) { 54 | var element = enumeration.nextElement(); 55 | if (element.isDirectory() || BLACKLISTED_FILES.matcher(element.getName()).find()) { 56 | continue; 57 | } 58 | 59 | var data = jarFile.getInputStream(element).readAllBytes(); 60 | if (element.getName().endsWith(".class")) { 61 | var reader = new ClassReader(data); 62 | var writer = new ClassWriter(0); 63 | reader.accept(new MalwareClassVisitor(writer), 0); 64 | data = writer.toByteArray(); 65 | } 66 | 67 | written.put(element.getName(), data); 68 | } 69 | } 70 | 71 | try (var output = new JarOutputStream(new FileOutputStream(file))) { 72 | for (var entry : written.entrySet()) { 73 | output.putNextEntry(new JarEntry(entry.getKey())); 74 | output.write(entry.getValue()); 75 | } 76 | } 77 | } 78 | } 79 | --------------------------------------------------------------------------------