diff --git a/bungee/src/main/java/com/alterdekim/xcraft/auth/bungee/EncryptionResponsePacket.java b/bungee/src/main/java/com/alterdekim/xcraft/auth/bungee/EncryptionResponsePacket.java new file mode 100644 index 0000000..74d89aa --- /dev/null +++ b/bungee/src/main/java/com/alterdekim/xcraft/auth/bungee/EncryptionResponsePacket.java @@ -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 callback_handler = new Callback(){ + + @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); + } +} diff --git a/bungee/src/main/java/com/alterdekim/xcraft/auth/bungee/XCraft.java b/bungee/src/main/java/com/alterdekim/xcraft/auth/bungee/XCraft.java index fd63e45..be449b0 100644 --- a/bungee/src/main/java/com/alterdekim/xcraft/auth/bungee/XCraft.java +++ b/bungee/src/main/java/com/alterdekim/xcraft/auth/bungee/XCraft.java @@ -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 handler = new Callback(){ - - @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