├── 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 |
--------------------------------------------------------------------------------