Fixing MySQL Lib injection

This commit is contained in:
2026-04-06 15:06:02 -04:00
parent 9e68524bd7
commit 45d2a7eaa0
9 changed files with 536 additions and 32 deletions
+2 -2
View File
@@ -47,7 +47,7 @@
<path> <path>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.30</version> <version>1.18.44</version>
</path> </path>
</annotationProcessorPaths> </annotationProcessorPaths>
</configuration> </configuration>
@@ -196,4 +196,4 @@
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -272,4 +272,4 @@ public class AntiVPN {
private void registerCommands() { private void registerCommands() {
commands.add(new AntiVPNCommand()); commands.add(new AntiVPNCommand());
} }
} }
@@ -255,7 +255,6 @@ public class H2VPN implements VPNDatabase {
try { try {
String cidrString = set.getString("cidr_string"); String cidrString = set.getString("cidr_string");
AntiVPN.getInstance().getExecutor().log("CIDR String: %s", cidrString);
ips.add(new CIDRUtils(cidrString)); ips.add(new CIDRUtils(cidrString));
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
@@ -25,7 +25,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
@@ -36,12 +35,17 @@ public class MySQL {
public static void init() { public static void init() {
try { try {
if (conn == null || conn.isClosed()) { if (conn == null || conn.isClosed()) {
DriverManager.registerDriver(new Driver()); String url = "jdbc:mysql://" + AntiVPN.getInstance().getVpnConfig().getIp()
conn = DriverManager.getConnection("jdbc:mysql://" + AntiVPN.getInstance().getVpnConfig().getIp() + ":" + AntiVPN.getInstance().getVpnConfig().getPort()
+ ":" + AntiVPN.getInstance().getVpnConfig().getPort() + "/?useSSL=true&autoReconnect=true";
+ "/?useSSL=true&autoReconnect=true", Properties properties = new Properties();
AntiVPN.getInstance().getVpnConfig().getUsername(), properties.setProperty("user", AntiVPN.getInstance().getVpnConfig().getUsername());
AntiVPN.getInstance().getVpnConfig().getPassword()); properties.setProperty("password", AntiVPN.getInstance().getVpnConfig().getPassword());
conn = new Driver().connect(url, properties);
if (conn == null) {
throw new SQLException("MySQL driver did not accept URL: " + url);
}
conn.setAutoCommit(true); conn.setAutoCommit(true);
Query.use(conn); Query.use(conn);
Query.prepare("CREATE DATABASE IF NOT EXISTS `" Query.prepare("CREATE DATABASE IF NOT EXISTS `"
@@ -51,6 +55,7 @@ public class MySQL {
} }
} catch (Exception e) { } catch (Exception e) {
AntiVPN.getInstance().getExecutor().logException("Failed to load mysql: " + e.getMessage(), e); AntiVPN.getInstance().getExecutor().logException("Failed to load mysql: " + e.getMessage(), e);
throw new RuntimeException("Could not initialize MySQL connection", e);
} }
} }
@@ -21,9 +21,14 @@ import dev.brighten.antivpn.utils.NonnullByDefault;
import dev.brighten.antivpn.utils.Supplier; import dev.brighten.antivpn.utils.Supplier;
import dev.brighten.antivpn.utils.Suppliers; import dev.brighten.antivpn.utils.Suppliers;
import lombok.Getter; import lombok.Getter;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.RecordComponentVisitor;
import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.Remapper;
@@ -31,6 +36,7 @@ import java.io.*;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.*; import java.util.*;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
@@ -44,6 +50,8 @@ import java.util.jar.JarOutputStream;
@SuppressWarnings("CallToPrintStackTrace") @SuppressWarnings("CallToPrintStackTrace")
@NonnullByDefault @NonnullByDefault
public final class LibraryLoader { public final class LibraryLoader {
private static final int RELOCATION_FORMAT_VERSION = 5;
private static final String RELOCATION_METADATA_PATH = "META-INF/antivpn-relocation.properties";
@SuppressWarnings("Guava") @SuppressWarnings("Guava")
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = AntiVPN.getInstance().getClass().getClassLoader() instanceof URLClassLoader ? private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = AntiVPN.getInstance().getClass().getClassLoader() instanceof URLClassLoader ?
@@ -109,8 +117,8 @@ public final class LibraryLoader {
} }
} }
// If we have relocations, create a relocated jar // Rebuild relocated jars when the relocation format changes or the cached jar is stale.
if (!relocations.isEmpty() && !saveLocation.exists()) { if (!relocations.isEmpty() && shouldRebuildRelocatedJar(saveLocation, relocations)) {
try { try {
System.out.println("Relocating packages for " + name + "..."); System.out.println("Relocating packages for " + name + "...");
relocateJar(originalJar, saveLocation, relocations); relocateJar(originalJar, saveLocation, relocations);
@@ -142,6 +150,8 @@ public final class LibraryLoader {
// Track service files to avoid duplicates // Track service files to avoid duplicates
Map<String, StringBuilder> serviceFiles = new HashMap<>(); Map<String, StringBuilder> serviceFiles = new HashMap<>();
Files.deleteIfExists(targetJar.toPath());
try (JarFile jar = new JarFile(sourceJar); try (JarFile jar = new JarFile(sourceJar);
JarOutputStream jos = new JarOutputStream(Files.newOutputStream(targetJar.toPath()))) { JarOutputStream jos = new JarOutputStream(Files.newOutputStream(targetJar.toPath()))) {
@@ -168,12 +178,14 @@ public final class LibraryLoader {
jos.putNextEntry(newEntry); jos.putNextEntry(newEntry);
byte[] classBytes = readAllBytes(is); byte[] classBytes = readAllBytes(is);
byte[] relocatedBytes = relocateClass(classBytes, relocations); byte[] relocatedBytes = relocateClass(name, classBytes, relocations);
jos.write(relocatedBytes); jos.write(relocatedBytes);
jos.closeEntry(); jos.closeEntry();
} else { } else {
// Copy other files as-is // Relocate package-scoped resources so ResourceBundle lookups follow relocated packages.
JarEntry newEntry = new JarEntry(name); String relocatedPath = relocateResourcePath(name, relocations);
JarEntry newEntry = new JarEntry(relocatedPath);
jos.putNextEntry(newEntry); jos.putNextEntry(newEntry);
copyStream(is, jos); copyStream(is, jos);
jos.closeEntry(); jos.closeEntry();
@@ -186,7 +198,7 @@ public final class LibraryLoader {
try { try {
JarEntry serviceEntry = new JarEntry(entry.getKey()); JarEntry serviceEntry = new JarEntry(entry.getKey());
jos.putNextEntry(serviceEntry); jos.putNextEntry(serviceEntry);
jos.write(entry.getValue().toString().getBytes()); jos.write(entry.getValue().toString().getBytes(StandardCharsets.UTF_8));
jos.closeEntry(); jos.closeEntry();
} catch (Exception e) { } catch (Exception e) {
// Log but continue with other service files // Log but continue with other service files
@@ -194,7 +206,64 @@ public final class LibraryLoader {
entry.getKey() + ": " + e.getMessage()); entry.getKey() + ": " + e.getMessage());
} }
} }
writeRelocationMetadata(jos, relocations);
} }
validateRelocatedJar(targetJar, relocations);
}
private static boolean shouldRebuildRelocatedJar(File relocatedJar, Map<String, String> relocations) {
if (!relocatedJar.exists()) {
return true;
}
try (JarFile jar = new JarFile(relocatedJar)) {
JarEntry metadataEntry = jar.getJarEntry(RELOCATION_METADATA_PATH);
if (metadataEntry == null) {
return true;
}
Properties metadata = new Properties();
try (InputStream is = jar.getInputStream(metadataEntry)) {
metadata.load(is);
}
if (!String.valueOf(RELOCATION_FORMAT_VERSION).equals(metadata.getProperty("formatVersion"))) {
return true;
}
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
String key = "relocation." + relocation.getKey();
if (!relocation.getValue().equals(metadata.getProperty(key))) {
return true;
}
}
return Integer.toString(relocations.size()).equals(metadata.getProperty("relocationCount"));
} catch (IOException e) {
return true;
}
}
private static void writeRelocationMetadata(JarOutputStream jos, Map<String, String> relocations)
throws IOException {
Properties metadata = new Properties();
metadata.setProperty("formatVersion", Integer.toString(RELOCATION_FORMAT_VERSION));
metadata.setProperty("relocationCount", Integer.toString(relocations.size()));
Map<String, String> sortedRelocations = new TreeMap<>(relocations);
for (Map.Entry<String, String> relocation : sortedRelocations.entrySet()) {
metadata.setProperty("relocation." + relocation.getKey(), relocation.getValue());
}
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
metadata.store(buffer, "AntiVPN relocation metadata");
JarEntry metadataEntry = new JarEntry(RELOCATION_METADATA_PATH);
jos.putNextEntry(metadataEntry);
jos.write(buffer.toByteArray());
jos.closeEntry();
} }
private static void processServiceFile(String name, InputStream is, private static void processServiceFile(String name, InputStream is,
@@ -220,7 +289,7 @@ public final class LibraryLoader {
} }
} }
private static byte[] relocateClass(byte[] classBytes, Map<String, String> relocations) { private static byte[] relocateClass(String entryName, byte[] classBytes, Map<String, String> relocations) {
try { try {
// Convert to slash notation for ASM // Convert to slash notation for ASM
Remapper prefixRemapper = getPrefixRemapper(relocations); Remapper prefixRemapper = getPrefixRemapper(relocations);
@@ -239,21 +308,87 @@ public final class LibraryLoader {
} }
}; };
ClassVisitor visitor = new ClassRemapper(writer, prefixRemapper); ClassVisitor visitor = createStringRelocationVisitor(new ClassRemapper(writer, prefixRemapper), relocations);
visitor = createMySqlUtilFallbackVisitor(entryName, visitor);
// Process class with remapper // Process class with remapper
reader.accept(visitor, ClassReader.EXPAND_FRAMES); reader.accept(visitor, 0);
return writer.toByteArray(); return relocateUtf8Constants(writer.toByteArray(), relocations);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throw new IllegalStateException("Failed to relocate class entry " + entryName, e);
return classBytes;
} }
} }
public static String relocateReflectiveClassName(String className) {
if (className == null || className.startsWith("dev.brighten.antivpn.shaded.")) {
return className;
}
if (className.startsWith("com.mysql.cj") || className.startsWith("com.mysql.jdbc")) {
return "dev.brighten.antivpn.shaded." + className;
}
return className;
}
private static byte[] relocateUtf8Constants(byte[] classBytes, Map<String, String> relocations) throws IOException {
Map<String, String> dotMappings = new HashMap<>();
Map<String, String> slashMappings = new HashMap<>();
for (Map.Entry<String, String> entry : relocations.entrySet()) {
dotMappings.put(entry.getKey(), entry.getValue());
slashMappings.put(entry.getKey().replace('.', '/'), entry.getValue().replace('.', '/'));
}
DataInputStream in = new DataInputStream(new ByteArrayInputStream(classBytes));
ByteArrayOutputStream baos = new ByteArrayOutputStream(classBytes.length + 256);
DataOutputStream out = new DataOutputStream(baos);
out.writeInt(in.readInt());
out.writeShort(in.readUnsignedShort());
out.writeShort(in.readUnsignedShort());
int constantPoolCount = in.readUnsignedShort();
out.writeShort(constantPoolCount);
for (int i = 1; i < constantPoolCount; i++) {
int tag = in.readUnsignedByte();
out.writeByte(tag);
switch (tag) {
case 1 -> {
String value = in.readUTF();
String relocated = relocateStringValue(value, dotMappings, slashMappings);
out.writeUTF(relocated);
}
case 3, 4 -> out.writeInt(in.readInt());
case 5, 6 -> {
out.writeLong(in.readLong());
i++;
}
case 7, 8, 16, 19, 20 -> out.writeShort(in.readUnsignedShort());
case 9, 10, 11, 12, 17, 18 -> {
out.writeShort(in.readUnsignedShort());
out.writeShort(in.readUnsignedShort());
}
case 15 -> {
out.writeByte(in.readUnsignedByte());
out.writeShort(in.readUnsignedShort());
}
default -> throw new IOException("Unknown constant pool tag " + tag);
}
}
copyStream(in, out);
out.flush();
return baos.toByteArray();
}
private static Remapper getPrefixRemapper(Map<String, String> relocations) { private static Remapper getPrefixRemapper(Map<String, String> relocations) {
Map<String, String> slashMappings = new HashMap<>(); Map<String, String> slashMappings = new HashMap<>();
Map<String, String> dotMappings = new HashMap<>();
for (Map.Entry<String, String> entry : relocations.entrySet()) { for (Map.Entry<String, String> entry : relocations.entrySet()) {
dotMappings.put(entry.getKey(), entry.getValue());
String fromSlash = entry.getKey().replace('.', '/'); String fromSlash = entry.getKey().replace('.', '/');
String toSlash = entry.getValue().replace('.', '/'); String toSlash = entry.getValue().replace('.', '/');
slashMappings.put(fromSlash, toSlash); slashMappings.put(fromSlash, toSlash);
@@ -275,9 +410,359 @@ public final class LibraryLoader {
} }
return typeName; return typeName;
} }
@Override
public Object mapValue(Object value) {
if (value instanceof String stringValue) {
return relocateStringValue(stringValue, dotMappings, slashMappings);
}
return super.mapValue(value);
}
}; };
} }
private static ClassVisitor createMySqlUtilFallbackVisitor(String entryName, ClassVisitor delegate) {
if (!"com/mysql/cj/util/Util.class".equals(entryName)) {
return delegate;
}
return new ClassVisitor(Opcodes.ASM9, delegate) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
String[] exceptions) {
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
if (visitor == null) {
return null;
}
if (!"getInstance".equals(name)
|| !"(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;[Ljava/lang/Object;Lcom/mysql/cj/exceptions/ExceptionInterceptor;)Ljava/lang/Object;".equals(descriptor)) {
return visitor;
}
return new MethodVisitor(Opcodes.ASM9, visitor) {
@Override
public void visitCode() {
super.visitCode();
super.visitVarInsn(Opcodes.ALOAD, 1);
super.visitMethodInsn(Opcodes.INVOKESTATIC,
"dev/brighten/antivpn/depends/LibraryLoader",
"relocateReflectiveClassName",
"(Ljava/lang/String;)Ljava/lang/String;",
false);
super.visitVarInsn(Opcodes.ASTORE, 1);
}
};
}
};
}
private static ClassVisitor createStringRelocationVisitor(ClassVisitor delegate,
Map<String, String> relocations) {
Map<String, String> dotMappings = new HashMap<>();
Map<String, String> slashMappings = new HashMap<>();
for (Map.Entry<String, String> entry : relocations.entrySet()) {
dotMappings.put(entry.getKey(), entry.getValue());
slashMappings.put(entry.getKey().replace('.', '/'), entry.getValue().replace('.', '/'));
}
return new ClassVisitor(Opcodes.ASM9, delegate) {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible), dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
dotMappings, slashMappings);
}
@Override
public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
RecordComponentVisitor visitor = super.visitRecordComponent(name, descriptor, signature);
if (visitor == null) {
return null;
}
return new RecordComponentVisitor(Opcodes.ASM9, visitor) {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
dotMappings, slashMappings);
}
};
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
FieldVisitor visitor = super.visitField(access, name, descriptor, signature,
relocateAsmValue(value, dotMappings, slashMappings));
if (visitor == null) {
return null;
}
return new FieldVisitor(Opcodes.ASM9, visitor) {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
dotMappings, slashMappings);
}
};
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
String[] exceptions) {
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
if (visitor == null) {
return null;
}
return new MethodVisitor(Opcodes.ASM9, visitor) {
@Override
public AnnotationVisitor visitAnnotationDefault() {
return wrapAnnotationVisitor(super.visitAnnotationDefault(), dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor,
boolean visible) {
return wrapAnnotationVisitor(super.visitParameterAnnotation(parameter, descriptor, visible),
dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitInsnAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitInsnAnnotation(typeRef, typePath, descriptor, visible),
dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
String descriptor, boolean visible) {
return wrapAnnotationVisitor(super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible),
dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
org.objectweb.asm.TypePath typePath,
org.objectweb.asm.Label[] start,
org.objectweb.asm.Label[] end,
int[] index, String descriptor,
boolean visible) {
return wrapAnnotationVisitor(
super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible),
dotMappings, slashMappings);
}
@Override
public void visitLdcInsn(Object value) {
super.visitLdcInsn(relocateAsmValue(value, dotMappings, slashMappings));
}
@Override
public void visitInvokeDynamicInsn(String name, String descriptor, org.objectweb.asm.Handle bootstrapMethodHandle,
Object... bootstrapMethodArguments) {
Object[] relocatedArgs = new Object[bootstrapMethodArguments.length];
for (int i = 0; i < bootstrapMethodArguments.length; i++) {
relocatedArgs[i] = relocateAsmValue(bootstrapMethodArguments[i], dotMappings, slashMappings);
}
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, relocatedArgs);
}
};
}
};
}
private static AnnotationVisitor wrapAnnotationVisitor(AnnotationVisitor delegate,
Map<String, String> dotMappings,
Map<String, String> slashMappings) {
if (delegate == null) {
return null;
}
return new AnnotationVisitor(Opcodes.ASM9, delegate) {
@Override
public void visit(String name, Object value) {
super.visit(name, relocateAsmValue(value, dotMappings, slashMappings));
}
@Override
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
return wrapAnnotationVisitor(super.visitAnnotation(name, descriptor), dotMappings, slashMappings);
}
@Override
public AnnotationVisitor visitArray(String name) {
return wrapAnnotationVisitor(super.visitArray(name), dotMappings, slashMappings);
}
};
}
private static Object relocateAsmValue(Object value, Map<String, String> dotMappings,
Map<String, String> slashMappings) {
if (value instanceof String stringValue) {
return relocateStringValue(stringValue, dotMappings, slashMappings);
}
return value;
}
private static String relocateStringValue(String value, Map<String, String> dotMappings,
Map<String, String> slashMappings) {
for (Map.Entry<String, String> entry : dotMappings.entrySet()) {
String from = entry.getKey();
String relocated = relocateByPrefixes(value, from, entry.getValue(), '.', '$');
if (!relocated.equals(value)) {
return relocated;
}
}
for (Map.Entry<String, String> entry : slashMappings.entrySet()) {
String from = entry.getKey();
String to = entry.getValue();
String relocated = relocateByPrefixes(value, from, to, '/', '$');
if (!relocated.equals(value)) {
return relocated;
}
relocated = relocateByPrefixes(value, "/" + from, "/" + to, '/', '$');
if (!relocated.equals(value)) {
return relocated;
}
relocated = relocateByPrefixes(value, "L" + from, "L" + to, '/', '$', ';');
if (!relocated.equals(value)) {
return relocated;
}
relocated = relocateByPrefixes(value, "[L" + from, "[L" + to, '/', '$', ';');
if (!relocated.equals(value)) {
return relocated;
}
}
return value;
}
private static String relocateByPrefixes(String value, String from, String to, char... delimiters) {
if (value.equals(from)) {
return to;
}
for (char delimiter : delimiters) {
if (value.startsWith(from + delimiter)) {
return to + value.substring(from.length());
}
}
return value;
}
private static void validateRelocatedJar(File targetJar, Map<String, String> relocations) throws IOException {
Set<String> relocatedPrefixes = new HashSet<>();
Map<String, String> dotMappings = new HashMap<>();
Map<String, String> slashMappings = new HashMap<>();
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
relocatedPrefixes.add(relocation.getValue().replace('.', '/') + "/");
dotMappings.put(relocation.getKey(), relocation.getValue());
slashMappings.put(relocation.getKey().replace('.', '/'), relocation.getValue().replace('.', '/'));
}
try (JarFile jar = new JarFile(targetJar)) {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
continue;
}
boolean shouldValidate = false;
for (String relocatedPrefix : relocatedPrefixes) {
if (entry.getName().startsWith(relocatedPrefix)) {
shouldValidate = true;
break;
}
}
if (!shouldValidate) {
continue;
}
try (InputStream is = jar.getInputStream(entry)) {
findUnrelocatedConstant(entry.getName(), readAllBytes(is), dotMappings, slashMappings);
}
}
}
}
private static void findUnrelocatedConstant(String entryName, byte[] classBytes, Map<String, String> dotMappings,
Map<String, String> slashMappings) throws IOException {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(classBytes));
in.readInt();
in.readUnsignedShort();
in.readUnsignedShort();
int constantPoolCount = in.readUnsignedShort();
for (int i = 1; i < constantPoolCount; i++) {
int tag = in.readUnsignedByte();
switch (tag) {
case 1 -> {
String value = in.readUTF();
String relocated = relocateStringValue(value, dotMappings, slashMappings);
if (!value.equals(relocated)) {
throw new IOException("Relocated jar still contains original reference '" + value
+ "' in class entry " + entryName);
}
}
case 3, 4 -> in.readInt();
case 5, 6 -> {
in.readLong();
i++;
}
case 7, 8, 16, 19, 20 -> in.readUnsignedShort();
case 9, 10, 11, 12, 17, 18 -> {
in.readUnsignedShort();
in.readUnsignedShort();
}
case 15 -> {
in.readUnsignedByte();
in.readUnsignedShort();
}
default -> throw new IOException("Unknown constant pool tag " + tag + " while validating " + entryName);
}
}
}
private static String relocateClassPath(String path, Map<String, String> relocations) { private static String relocateClassPath(String path, Map<String, String> relocations) {
// Convert path to package format (replacing / with .) // Convert path to package format (replacing / with .)
String packagePath = path.substring(0, path.length() - 6).replace('/', '.'); String packagePath = path.substring(0, path.length() - 6).replace('/', '.');
@@ -294,6 +779,23 @@ public final class LibraryLoader {
return packagePath.replace('.', '/') + ".class"; return packagePath.replace('.', '/') + ".class";
} }
private static String relocateResourcePath(String path, Map<String, String> relocations) {
if (path.startsWith("META-INF/")) {
return path;
}
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
String fromPath = relocation.getKey().replace('.', '/');
String toPath = relocation.getValue().replace('.', '/');
if (path.startsWith(fromPath + "/")) {
return toPath + path.substring(fromPath.length());
}
}
return path;
}
private static byte[] readAllBytes(InputStream is) throws IOException { private static byte[] readAllBytes(InputStream is) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int bytesRead; int bytesRead;
+1 -1
View File
@@ -47,7 +47,7 @@
<path> <path>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.30</version> <version>1.18.44</version>
</path> </path>
</annotationProcessorPaths> </annotationProcessorPaths>
</configuration> </configuration>
+1 -1
View File
@@ -172,4 +172,4 @@
</resources> </resources>
</build> </build>
</project> </project>
@@ -31,7 +31,6 @@ import org.bstats.velocity.Metrics;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -41,7 +40,7 @@ public class VelocityPlugin implements LoaderBootstrap {
private final ProxyServer server; private final ProxyServer server;
private final Logger logger; private final Logger logger;
private final Metrics.Factory metricsFactory; private final Metrics.Factory metricsFactory;
private final Path configDir; private File dataFolder;
@Nullable @Nullable
private Metrics metrics; private Metrics metrics;
@@ -54,7 +53,6 @@ public class VelocityPlugin implements LoaderBootstrap {
public VelocityPlugin(Map<Class<?>, Object> objectsMap) { public VelocityPlugin(Map<Class<?>, Object> objectsMap) {
this.server = (ProxyServer) objectsMap.get(ProxyServer.class); this.server = (ProxyServer) objectsMap.get(ProxyServer.class);
this.logger = (Logger) objectsMap.get(Logger.class); this.logger = (Logger) objectsMap.get(Logger.class);
this.configDir = (Path) objectsMap.get(Path.class);
this.metricsFactory = (Metrics.Factory) objectsMap.get(String.class); this.metricsFactory = (Metrics.Factory) objectsMap.get(String.class);
this.pluginInstance = objectsMap.get(LoaderBootstrap.class); this.pluginInstance = objectsMap.get(LoaderBootstrap.class);
} }
@@ -74,7 +72,7 @@ public class VelocityPlugin implements LoaderBootstrap {
@Override @Override
public void onLoad(File dataFolder) { public void onLoad(File dataFolder) {
this.dataFolder = dataFolder;
} }
@Override @Override
@@ -84,7 +82,7 @@ public class VelocityPlugin implements LoaderBootstrap {
//Loading plugin //Loading plugin
logger.info("Starting AntiVPN services..."); logger.info("Starting AntiVPN services...");
AntiVPN.start(new VelocityListener(), new VelocityPlayerExecutor(), configDir.toFile()); AntiVPN.start(new VelocityListener(), new VelocityPlayerExecutor(), dataFolder);
if(AntiVPN.getInstance().getVpnConfig().metrics()) { if(AntiVPN.getInstance().getVpnConfig().metrics()) {
+2 -2
View File
@@ -58,7 +58,7 @@
<path> <path>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.30</version> <version>1.18.44</version>
</path> </path>
</annotationProcessorPaths> </annotationProcessorPaths>
</configuration> </configuration>
@@ -83,7 +83,7 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.30</version> <version>1.18.44</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>