From 5df277f45b44ed171cbe050d9eb4dbc733628eb7 Mon Sep 17 00:00:00 2001 From: carm Date: Wed, 3 Jan 2024 13:22:09 +0800 Subject: [PATCH] feat(inject): Finished injectors --- .../BungeeAuthProxyInjector.java | 32 +--- .../injector/bungeeauthproxy/Config.java | 10 +- .../injector/bungeeauthproxy/Logging.java | 16 ++ .../handler/ProxiedAuthHandler.java | 90 ++++------ .../transformer/ProxyHandlerTransformer.java | 46 +++-- src/test/java/general/InitialHandler.java | 158 ------------------ 6 files changed, 73 insertions(+), 279 deletions(-) delete mode 100644 src/test/java/general/InitialHandler.java diff --git a/src/main/java/com/artformgames/injector/bungeeauthproxy/BungeeAuthProxyInjector.java b/src/main/java/com/artformgames/injector/bungeeauthproxy/BungeeAuthProxyInjector.java index 4aea97c..473b2dd 100644 --- a/src/main/java/com/artformgames/injector/bungeeauthproxy/BungeeAuthProxyInjector.java +++ b/src/main/java/com/artformgames/injector/bungeeauthproxy/BungeeAuthProxyInjector.java @@ -4,11 +4,7 @@ import com.artformgames.injector.bungeeauthproxy.handler.ProxiedAuthHandler; import com.artformgames.injector.bungeeauthproxy.transformer.ProxyHandlerTransformer; import io.netty.channel.EventLoop; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.CtMethod; import net.md_5.bungee.api.Callback; -import net.md_5.bungee.connection.InitialHandler; import java.lang.instrument.Instrumentation; @@ -25,35 +21,19 @@ private BungeeAuthProxyInjector() { public static void premain(String args, Instrumentation instrumentation) { BungeeAuthProxyInjector.instance = instrumentation; log(Logging.Level.INFO, "Loading auth configurations..."); - EasyConfiguration.from("auth.yml").initialize(Config.class); + String configFileName = args == null || args.isBlank() ? "auth.yml" : args; + EasyConfiguration.from(configFileName).initialize(Config.class); log(Logging.Level.INFO, "Initializing auth handler..."); handler = new ProxiedAuthHandler(); log(Logging.Level.INFO, "Injecting InitialHandler..."); - instrumentation.addTransformer(new ProxyHandlerTransformer()); - - - try { - ClassPool pool = ClassPool.getDefault(); - pool.importPackage("com.artformgames.injector.bungeeauthproxy"); - - CtClass handlerClass = pool.getCtClass("net.md_5.bungee.connection.InitialHandler"); - CtClass responseClass = pool.getCtClass("net.md_5.bungee.protocol.packet.EncryptionResponse"); - - CtMethod handleMethod = handlerClass.getDeclaredMethod("handle", new CtClass[]{responseClass}); - log(Logging.Level.DEBUG, "Found target method: " + handleMethod.getLongName()); - } catch (Exception ex) { - log(Logging.Level.ERROR, "Failed to inject handlers, are you really using BungeeCord?"); - ex.printStackTrace(); - } - + instrumentation.addTransformer(new ProxyHandlerTransformer(), true); } - public static void submitRequest(InitialHandler handler, String encodedHash, - EventLoop loop, Callback callback) throws Exception { - log(Logging.Level.DEBUG, "Submitting request [" + handler.getName() + "] " + encodedHash); - getHandler().submit(handler, encodedHash, loop, callback); + public static void submitRequest(String url, EventLoop loop, Callback callback) throws Exception { + log(Logging.Level.DEBUG, "Submitting request [" + url + "]"); + getHandler().submit(url, loop, callback); } public static Instrumentation getInstance() { diff --git a/src/main/java/com/artformgames/injector/bungeeauthproxy/Config.java b/src/main/java/com/artformgames/injector/bungeeauthproxy/Config.java index 4184e14..f6d855d 100644 --- a/src/main/java/com/artformgames/injector/bungeeauthproxy/Config.java +++ b/src/main/java/com/artformgames/injector/bungeeauthproxy/Config.java @@ -15,11 +15,6 @@ public interface Config extends Configuration { @HeaderComment("MineCraft service settings") interface SERVICE extends Configuration { - @HeaderComment("The authentication url for minecraft.net") - ConfiguredValue MOJANG_AUTH_URL = ConfiguredValue.of( - "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s%s" - ); - @HeaderComment("Timeout duration for single request in milliseconds.") ConfiguredValue TIME_OUT = ConfiguredValue.of(5000L); @@ -34,10 +29,7 @@ interface SERVICE extends Configuration { @HeaderComment("Proxy server settings") interface PROXY extends Configuration { - @HeaderComment("Whether to enable proxy to access the authentication services") - ConfiguredValue ENABLE = ConfiguredValue.of(true); - - @HeaderComment("Proxy protocol, 0 = HTTP/HTTPS, 1 = SOCKS4, 2 = SOCKS5") + @HeaderComment("Proxy protocol, -1 = NO_PROXY ,0 = HTTP/HTTPS, 1 = SOCKS4, 2 = SOCKS5") ConfiguredValue PROTOCOL = ConfiguredValue.of(1); @HeaderComment("Proxy host") diff --git a/src/main/java/com/artformgames/injector/bungeeauthproxy/Logging.java b/src/main/java/com/artformgames/injector/bungeeauthproxy/Logging.java index 261f5ce..48e6fa5 100644 --- a/src/main/java/com/artformgames/injector/bungeeauthproxy/Logging.java +++ b/src/main/java/com/artformgames/injector/bungeeauthproxy/Logging.java @@ -34,6 +34,22 @@ public static void log(Level level, String message) { log(level, message, null); } + public static void debug(String message) { + log(Level.DEBUG, message); + } + + public static void error(String message) { + log(Level.ERROR, message); + } + + public static void error(String message, Throwable e) { + log(Level.ERROR, message, e); + } + + public static void warning(String message) { + log(Level.WARNING, message); + } + public static void log(Level level, String message, Throwable e) { if (level == Level.DEBUG && !Config.DEBUG.getNotNull()) return; diff --git a/src/main/java/com/artformgames/injector/bungeeauthproxy/handler/ProxiedAuthHandler.java b/src/main/java/com/artformgames/injector/bungeeauthproxy/handler/ProxiedAuthHandler.java index 9d1abd7..03feecc 100644 --- a/src/main/java/com/artformgames/injector/bungeeauthproxy/handler/ProxiedAuthHandler.java +++ b/src/main/java/com/artformgames/injector/bungeeauthproxy/handler/ProxiedAuthHandler.java @@ -10,16 +10,17 @@ import io.netty.channel.EventLoop; import io.netty.handler.codec.http.*; import jline.internal.Nullable; -import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.Callback; -import net.md_5.bungee.connection.InitialHandler; -import net.md_5.bungee.http.HttpClient; +import net.md_5.bungee.http.HttpInitializer; import net.md_5.bungee.netty.PipelineUtils; -import java.net.*; -import java.nio.charset.StandardCharsets; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; +import static com.artformgames.injector.bungeeauthproxy.Logging.debug; + public class ProxiedAuthHandler { protected Cache addressCache; @@ -32,52 +33,39 @@ public ProxiedAuthHandler() { } } - public void submit(InitialHandler handler, String encodedHash, EventLoop loop, Callback callback) throws Exception { - submit(encodeName(handler.getName()), encodedHash, getPreventProxyParams(handler.getAddress()), loop, callback); - } - - public void submit(String encodedName, String encodedHash, String preventProxy, - EventLoop loop, Callback callback) - throws Exception { - submit(getAuthURL(encodedName, encodedHash, preventProxy), loop, callback); - } - public void submit(String authURL, EventLoop loop, Callback callback) throws Exception { - if (!Config.PROXY.ENABLE.getNotNull()) { // Proxy disabled - HttpClient.get(authURL, loop, callback); - return; - } - URI uri = new URI(authURL); Preconditions.checkNotNull((Object) uri.getScheme(), "scheme"); Preconditions.checkNotNull((Object) uri.getHost(), "host"); boolean ssl = uri.getScheme().equals("https"); int port = getPort(uri); - ProxyProtocolType proxyProtocol = getProxyProtocol(); - InetAddress address = resolveAddress(proxyProtocol, uri.getHost()); - - ChannelFutureListener listener = channel -> { - if (channel.isSuccess()) { - String path = uri.getRawPath() + (uri.getRawQuery() == null ? "" : "?" + uri.getRawQuery()); - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); - request.headers().set(HttpHeaderNames.HOST, uri.getHost()); - channel.channel().writeAndFlush(request); - } else { - addressCache.invalidate(uri.getHost()); - callback.done(null, channel.cause()); - } - }; + Bootstrap bootstrap = new Bootstrap().channel(PipelineUtils.getChannel(null)).group(loop); - new Bootstrap().channel(PipelineUtils.getChannel(null)).group(loop) - .handler(new ProxiedHttpInitializer(proxyProtocol, callback, ssl, uri.getHost(), port)) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.max(Config.SERVICE.TIME_OUT.getNotNull().intValue(), 100)) - .remoteAddress(address, port) - .connect().addListener(listener); + InetAddress address = resolveAddress(uri.getHost()); + ProxyProtocolType proxyProtocol = getProxyProtocol(); + if (proxyProtocol != null) { + debug("Using proxy protocol [" + proxyProtocol.name() + "] for " + uri.getHost() + ":" + port); + bootstrap.handler(new ProxiedHttpInitializer(proxyProtocol, callback, ssl, uri.getHost(), port)); + } else { + bootstrap.handler(new HttpInitializer(callback, ssl, uri.getHost(), port)); + } + bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.max(Config.SERVICE.TIME_OUT.getNotNull().intValue(), 100)) + .remoteAddress(address, port).connect().addListener((ChannelFutureListener) channel -> { + if (channel.isSuccess()) { + String path = uri.getRawPath() + (uri.getRawQuery() == null ? "" : "?" + uri.getRawQuery()); + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); + request.headers().set(HttpHeaderNames.HOST, uri.getHost()); + channel.channel().writeAndFlush(request); + } else { + addressCache.invalidate(uri.getHost()); + callback.done(null, channel.cause()); + } + }); } - private InetAddress resolveAddress(@Nullable ProxyProtocolType protocol, String host) throws UnknownHostException { - if (protocol == null || this.addressCache == null) return InetAddress.getByName(host); + private InetAddress resolveAddress(String host) throws UnknownHostException { + if (this.addressCache == null) return InetAddress.getByName(host); InetAddress inetHost = addressCache.getIfPresent(host); if (inetHost == null) { @@ -91,15 +79,6 @@ private InetAddress resolveAddress(@Nullable ProxyProtocolType protocol, String return ProxyProtocolType.parse(Config.PROXY.PROTOCOL.getNotNull()); } - public String getAuthURL(String encodedName, String encodedHash, String preventProxy) { - try { - return String.format(Config.SERVICE.MOJANG_AUTH_URL.getNotNull(), encodedName, encodedHash, preventProxy); - } catch (Exception e) { - e.printStackTrace(); - return "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + encodedName + "&serverId=" + encodedHash + preventProxy; - } - } - private static int getPort(URI uri) { int port = uri.getPort(); if (port != -1) return port; @@ -116,15 +95,4 @@ private static int getPort(URI uri) { } } - protected static String encodeName(String name) { - return URLEncoder.encode(name, StandardCharsets.UTF_8); - } - - protected static String getPreventProxyParams(SocketAddress address) { - if (!BungeeCord.getInstance().config.isPreventProxyConnections() || !(address instanceof InetSocketAddress)) { - return ""; - } - return URLEncoder.encode(((InetSocketAddress) address).getAddress().getHostAddress(), StandardCharsets.UTF_8); - } - } diff --git a/src/main/java/com/artformgames/injector/bungeeauthproxy/transformer/ProxyHandlerTransformer.java b/src/main/java/com/artformgames/injector/bungeeauthproxy/transformer/ProxyHandlerTransformer.java index e508ce0..a25d0ba 100644 --- a/src/main/java/com/artformgames/injector/bungeeauthproxy/transformer/ProxyHandlerTransformer.java +++ b/src/main/java/com/artformgames/injector/bungeeauthproxy/transformer/ProxyHandlerTransformer.java @@ -3,43 +3,39 @@ import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; +import javassist.LoaderClassPath; import java.io.ByteArrayInputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; +import static com.artformgames.injector.bungeeauthproxy.Logging.debug; +import static com.artformgames.injector.bungeeauthproxy.Logging.error; public class ProxyHandlerTransformer implements ClassFileTransformer { - @Override - public byte[] transform(ClassLoader loader, String className, - Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classFileBuffer) throws IllegalClassFormatException { - if (!className.equals("net/md_5/bungee/connection/InitialHandler")) return classFileBuffer; - - System.out.println("Handling: " + className); - - byte[] transformed = null; - ClassPool pool = ClassPool.getDefault(); - CtClass cl = null; + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classFileBuffer) throws IllegalClassFormatException { + if (!className.equals("net/md_5/bungee/http/HttpClient")) return classFileBuffer; try { - cl = pool.makeClass(new ByteArrayInputStream(classFileBuffer)); - CtClass responseClass = pool.getCtClass("net.md_5.bungee.protocol.packet.EncryptionResponse"); - - CtMethod handleMethod = cl.getDeclaredMethod("handle", new CtClass[]{responseClass}); - handleMethod.insertBefore("System.out.println(\"Successfully loaded!!!!!\");"); - - transformed = cl.toBytecode(); - + ClassPool pool = ClassPool.getDefault(); + pool.insertClassPath(new LoaderClassPath(loader)); + CtClass clazz = pool.makeClass(new ByteArrayInputStream(classFileBuffer)); + CtClass callbackClass = pool.getCtClass("net.md_5.bungee.api.Callback"); + CtClass eventLoopClass = pool.getCtClass("io.netty.channel.EventLoop"); + CtClass stringClass = pool.getCtClass("java.lang.String"); + + CtMethod handleMethod = clazz.getDeclaredMethod("get", new CtClass[]{stringClass, eventLoopClass, callbackClass}); + debug("Injecting into " + handleMethod.getLongName()); + + handleMethod.setBody("{com.artformgames.injector.bungeeauthproxy.BungeeAuthProxyInjector.submitRequest($1, $2, $3);}"); + return clazz.toBytecode(); } catch (Exception e) { - e.printStackTrace(); - } finally { - if (cl != null) { - cl.detach(); - } + error("Failed to inject handlers into [" + className + "], are you really using BungeeCord?", e); + return classFileBuffer; } - - return transformed; } } \ No newline at end of file diff --git a/src/test/java/general/InitialHandler.java b/src/test/java/general/InitialHandler.java deleted file mode 100644 index 1fd1d5f..0000000 --- a/src/test/java/general/InitialHandler.java +++ /dev/null @@ -1,158 +0,0 @@ - -// -// Source code recreated from a .class file by IntelliJ IDEA -// (powered by FernFlower decompiler) -// - -package general; - -import com.artformgames.injector.bungeeauthproxy.BungeeAuthProxyInjector; -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.config.ListenerInfo; -import net.md_5.bungee.api.connection.Connection; -import net.md_5.bungee.api.connection.PendingConnection; -import net.md_5.bungee.connection.LoginResult; -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.*; - -import javax.crypto.SecretKey; -import java.math.BigInteger; -import java.net.InetSocketAddress; -import java.net.URLEncoder; -import java.security.MessageDigest; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import java.util.logging.Level; - -public class InitialHandler extends net.md_5.bungee.connection.InitialHandler implements PendingConnection { - private final BungeeCord bungee; - private ChannelWrapper ch; - private final ListenerInfo listener; - private Handshake handshake; - private LoginRequest loginRequest; - private EncryptionRequest request; - private PluginMessage brandMessage; - private final Set registeredChannels = new HashSet<>(); - private State thisState; - private final Connection.Unsafe unsafe; - private boolean onlineMode; - private InetSocketAddress virtualHost; - private String name; - private UUID uniqueId; - private UUID offlineId; - private LoginResult loginProfile; - private boolean legacy; - private String extraDataInHandshake; - - public InitialHandler(BungeeCord bungee, ListenerInfo listener, BungeeCord bungee1, ListenerInfo listener1, Unsafe unsafe) { - super(bungee, listener); - this.bungee = bungee1; - this.listener = listener1; - this.unsafe = unsafe; - } - - public void handle(EncryptionResponse encryptResponse) throws Exception { - Preconditions.checkState(this.thisState == InitialHandler.State.ENCRYPT, "Not expecting ENCRYPT"); - SecretKey sharedKey = EncryptionUtil.getSecret(encryptResponse, this.request); - BungeeCipher decrypt = EncryptionUtil.getCipher(false, sharedKey); - this.ch.addBefore("frame-decoder", "decrypt", new CipherDecoder(decrypt)); - BungeeCipher encrypt = EncryptionUtil.getCipher(true, sharedKey); - this.ch.addBefore("frame-prepender", "encrypt", new CipherEncoder(encrypt)); - String encName = URLEncoder.encode(this.getName(), "UTF-8"); - MessageDigest sha = MessageDigest.getInstance("SHA-1"); - byte[][] data = new byte[][]{this.request.getServerId().getBytes("ISO_8859_1"), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded()}; - for (byte[] datum : data) { - sha.update(datum); - } - - String encodedHash = URLEncoder.encode((new BigInteger(sha.digest())).toString(16), "UTF-8"); - this.thisState = InitialHandler.State.FINISHING; - BungeeAuthProxyInjector.submitRequest(this, encodedHash, this.ch.getHandle().eventLoop(), (result, error) -> { - if (error == null) { - LoginResult obj = BungeeCord.getInstance().gson.fromJson(result, LoginResult.class); - if (obj != null && obj.getId() != null) { - InitialHandler.this.loginProfile = obj; - InitialHandler.this.name = obj.getName(); - InitialHandler.this.uniqueId = Util.getUUID(obj.getId()); - InitialHandler.this.finish(); - return; - } - InitialHandler.this.disconnect(InitialHandler.this.bungee.getTranslation("offline_mode_player", new Object[0])); - } else { - InitialHandler.this.disconnect(InitialHandler.this.bungee.getTranslation("mojang_fail", new Object[0])); - InitialHandler.this.bungee.getLogger().log(Level.SEVERE, "Error authenticating " + InitialHandler.this.getName() + " with minecraft.net", error); - } - }); - } - - public void finish() { - - } - - public ListenerInfo getListener() { - return this.listener; - } - - public Handshake getHandshake() { - return this.handshake; - } - - public LoginRequest getLoginRequest() { - return this.loginRequest; - } - - public PluginMessage getBrandMessage() { - return this.brandMessage; - } - - public Set getRegisteredChannels() { - return this.registeredChannels; - } - - public boolean isOnlineMode() { - return this.onlineMode; - } - - public InetSocketAddress getVirtualHost() { - return this.virtualHost; - } - - public UUID getUniqueId() { - return this.uniqueId; - } - - public UUID getOfflineId() { - return this.offlineId; - } - - public LoginResult getLoginProfile() { - return this.loginProfile; - } - - public boolean isLegacy() { - return this.legacy; - } - - public String getExtraDataInHandshake() { - return this.extraDataInHandshake; - } - - private enum State { - HANDSHAKE, - STATUS, - PING, - USERNAME, - ENCRYPT, - FINISHING; - - State() { - } - } -}