diff --git a/bungee/pom.xml b/bungee/pom.xml
index 9a8047e..69d9a3b 100644
--- a/bungee/pom.xml
+++ b/bungee/pom.xml
@@ -11,8 +11,8 @@
- bungeecord-repo
- https://oss.sonatype.org/content/repositories/snapshots
+ local-maven
+ file://${user.home}/.m2/repository
@@ -26,6 +26,12 @@
${bungee.api.version}
provided
+
+ net.md-5
+ bungeecord-proxy
+ ${bungee.api.version}
+ provided
+
com.alterdekim.xcraft.auth
common
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 89dc269..fd63e45 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,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 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) {
@@ -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());