Bungeecord support added x6

This commit is contained in:
Michael Wain 2025-03-21 18:33:19 +03:00
parent 9fd2dc825a
commit 27a8d0a72b
2 changed files with 97 additions and 39 deletions

View File

@ -11,8 +11,8 @@
<repositories>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<id>local-maven</id>
<url>file://${user.home}/.m2/repository</url>
</repository>
</repositories>
@ -26,6 +26,12 @@
<version>${bungee.api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>${bungee.api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alterdekim.xcraft.auth</groupId>
<artifactId>common</artifactId>

View File

@ -1,6 +1,10 @@
package com.alterdekim.xcraft.auth.bungee;
import com.alterdekim.xcraft.auth.SaltNic;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.EncryptionUtil;
import net.md_5.bungee.Util;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.PreLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
@ -8,18 +12,28 @@ import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.event.EventHandler;
import sun.misc.Unsafe;
import net.md_5.bungee.http.HttpClient;
import net.md_5.bungee.jni.cipher.BungeeCipher;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.cipher.CipherDecoder;
import net.md_5.bungee.netty.cipher.CipherEncoder;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
import net.md_5.bungee.protocol.packet.EncryptionResponse;
import javax.crypto.SecretKey;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.UUID;
import static com.alterdekim.xcraft.auth.lib.Patcher.patchAuthLib;
import java.security.MessageDigest;
import java.util.logging.Level;
public class XCraft extends Plugin implements Listener {
@ -59,15 +73,80 @@ public class XCraft extends Plugin implements Listener {
PendingConnection connection = event.getConnection();
if (!connection.getClass().getName().equals("net.md_5.bungee.connection.InitialHandler")) return;
if( !server.getSessionValue(connection.getUniqueId().toString()) ) return;
Class<?> initialHandlerClass = connection.getClass();
Field onlineModeField = initialHandlerClass.getDeclaredField("onlineMode");
onlineModeField.setAccessible(true);
onlineModeField.set(connection, false);
injectCustomSkin(connection);
Method hasJoinedMethod = initialHandlerClass.getDeclaredMethod("handle", EncryptionResponse.class);
hasJoinedMethod.setAccessible(true);
Method finish = initialHandlerClass.getDeclaredMethod("finish");
finish.setAccessible(true);
Field ch = initialHandlerClass.getDeclaredField("ch");
ch.setAccessible(true);
Field request = initialHandlerClass.getDeclaredField("request");
request.setAccessible(true);
Field loginProfile = initialHandlerClass.getDeclaredField("loginProfile");
loginProfile.setAccessible(true);
Field name = initialHandlerClass.getDeclaredField("name");
name.setAccessible(true);
Field uniqueId = initialHandlerClass.getDeclaredField("uniqueId");
uniqueId.setAccessible(true);
InitialHandler initialHandler = (InitialHandler) connection;
Object proxy = Proxy.newProxyInstance(
initialHandlerClass.getClassLoader(),
new Class[]{initialHandlerClass},
(proxy1, method, args) -> {
if (method.getName().equals("handle") && args.length == 1 && args[0].getClass() == EncryptionResponse.class) {
getLogger().info("Intercepted hasJoined request, returning fake response...");
SecretKey sharedKey = EncryptionUtil.getSecret((EncryptionResponse) args[0], (EncryptionRequest) request.get(initialHandler));
BungeeCipher decrypt = EncryptionUtil.getCipher(false, sharedKey);
((ChannelWrapper)ch.get(initialHandler)).addBefore("frame-decoder", "decrypt", new CipherDecoder(decrypt));
BungeeCipher encrypt = EncryptionUtil.getCipher(true, sharedKey);
((ChannelWrapper)ch.get(initialHandler)).addBefore("frame-prepender", "encrypt", new CipherEncoder(encrypt));
String encName = URLEncoder.encode(initialHandler.getName(), "UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
for (byte[] bit : new byte[][]{((EncryptionRequest) request.get(initialHandler)).getServerId().getBytes("ISO_8859_1"), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded()}) {
sha.update(bit);
}
String authURL = "http://localhost:"+INTERNAL_PORT+"/api/hasJoined?username=" + encName;
Callback<String> handler = new Callback<String>(){
@Override
public void done(String result, Throwable error) {
if (error == null) {
LoginResult obj = BungeeCord.getInstance().gson.fromJson(result, LoginResult.class);
if (obj != null && obj.getId() != null) {
try {
loginProfile.set(initialHandler, obj);
name.set(initialHandler, obj.getName());
uniqueId.set(initialHandler, Util.getUUID(obj.getId()));
finish.invoke(initialHandler);
return;
} catch (Exception e) {
getLogger().log(Level.SEVERE, "Error authenticating " + initialHandler.getName() + " with XCraftAuth", e);
}
}
initialHandler.disconnect("You're in offline mode");
} else {
initialHandler.disconnect("XCraftAuth has failed to authenticate you");
getLogger().log(Level.SEVERE, "Error authenticating " + initialHandler.getName() + " with XCraftAuth", error);
}
}
};
HttpClient.get(authURL, ((ChannelWrapper)ch.get(initialHandler)).getHandle().eventLoop(), handler);
return new Object();
}
return method.invoke(connection, args);
}
);
getLogger().info("Bypassed Mojang authentication for " + connection.getName());
} catch (Exception e) {
@ -75,32 +154,6 @@ public class XCraft extends Plugin implements Listener {
}
}
private void injectCustomSkin(PendingConnection connection) {
try {
Class<?> initialHandlerClass = connection.getClass();
Field loginProfileField = initialHandlerClass.getDeclaredField("loginProfile");
loginProfileField.setAccessible(true);
Class<?> loginResultClass = Class.forName("net.md_5.bungee.connection.LoginResult");
Class<?> propertyClass = Class.forName("net.md_5.bungee.connection.LoginResult$Property");
Object[] properties = new Object[1];
properties[0] = propertyClass.getConstructor(String.class, String.class, String.class)
.newInstance("textures", server.getTextures(connection.getUniqueId().toString()).getValue(), null);
Object customLoginResult = loginResultClass
.getConstructor(String.class, UUID.class, propertyClass.arrayType())
.newInstance(connection.getName(), connection.getUniqueId(), properties);
loginProfileField.set(connection, customLoginResult);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onDisable() {
if (server != null) {
@ -117,7 +170,6 @@ public class XCraft extends Plugin implements Listener {
File file = new File(getDataFolder(), "config.yml");
if (!file.exists()) {
try (InputStream in = getResourceAsStream("config.yml")) {
Files.copy(in, file.toPath());