Bungeecord support added x7

This commit is contained in:
Michael Wain 2025-03-21 21:21:04 +03:00
parent 27a8d0a72b
commit 57e2584f7e
2 changed files with 126 additions and 82 deletions

View File

@ -0,0 +1,103 @@
package com.alterdekim.xcraft.auth.bungee;
import com.google.common.base.Preconditions;
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.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
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.AbstractPacketHandler;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
import net.md_5.bungee.protocol.packet.EncryptionResponse;
import javax.crypto.SecretKey;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.alterdekim.xcraft.auth.bungee.XCraft.INTERNAL_PORT;
public class EncryptionResponsePacket extends EncryptionResponse {
private final Logger logger;
public EncryptionResponsePacket(Logger logger) {
this.logger = logger;
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception {
this.logger.info("Intercepted handle request, returning custom response...");
InitialHandler initialHandler = (InitialHandler) handler;
Class<?> initialHandlerClass = InitialHandler.class;
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);
Field thisState = initialHandlerClass.getDeclaredField("thisState");
thisState.setAccessible(true);
Preconditions.checkState( thisState.get(handler).toString().equals("ENCRYPT"), "Not expecting ENCRYPT" );
SecretKey sharedKey = EncryptionUtil.getSecret(this, (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> callback_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) {
EncryptionResponsePacket.this.logger.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");
EncryptionResponsePacket.this.logger.log(Level.SEVERE, "Error authenticating " + initialHandler.getName() + " with XCraftAuth", error);
}
}
};
HttpClient.get(authURL, ((ChannelWrapper)ch.get(initialHandler)).getHandle().eventLoop(), callback_handler);
}
}

View File

@ -1,6 +1,8 @@
package com.alterdekim.xcraft.auth.bungee;
import com.alterdekim.xcraft.auth.SaltNic;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.TObjectIntMap;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.EncryptionUtil;
import net.md_5.bungee.Util;
@ -20,6 +22,7 @@ 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.Protocol;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
import net.md_5.bungee.protocol.packet.EncryptionResponse;
@ -34,6 +37,7 @@ import java.net.URLEncoder;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.util.logging.Level;
import java.util.logging.Logger;
public class XCraft extends Plugin implements Listener {
@ -67,91 +71,28 @@ public class XCraft extends Plugin implements Listener {
}
}
@EventHandler
public void onPreLogin(PreLoginEvent event) {
try {
PendingConnection connection = event.getConnection();
@SuppressWarnings("unchecked")
private void injectListener(int version) throws Exception {
Field protocolsField = Protocol.LOGIN.TO_SERVER.getClass().getDeclaredField("protocols");
protocolsField.setAccessible(true);
TIntObjectMap<?> protocols = (TIntObjectMap)protocolsField.get(Protocol.LOGIN.TO_SERVER);
if (!connection.getClass().getName().equals("net.md_5.bungee.connection.InitialHandler")) return;
Object protocolData = protocols.get(version);
Field packetMapField = protocolData.getClass().getDeclaredField("packetMap");
Field packetConstructorsField = protocolData.getClass().getDeclaredField("packetConstructors");
packetMapField.setAccessible(true);
packetConstructorsField.setAccessible(true);
TObjectIntMap packetMap = (TObjectIntMap)packetMapField.get(protocolData);
TIntObjectMap packetConstructors = (TIntObjectMap)packetConstructorsField.get(protocolData);
Class<?> initialHandlerClass = connection.getClass();
packetMap.remove(EncryptionResponse.class);
packetConstructors.remove(0x01);
packetMap.put( EncryptionResponsePacket.class, 0x01);
packetConstructors.put( 0x01, EncryptionResponsePacket.class.getDeclaredConstructor(Logger.class).newInstance(getLogger()) );
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) {
e.printStackTrace();
}
packetMapField.set(protocolData, packetMap);
packetConstructorsField.set(protocolData, packetConstructors);
protocolsField.set(Protocol.LOGIN.TO_SERVER, protocols);
}
@Override