Fix fucking bukkit injection
Dieser Commit ist enthalten in:
Ursprung
65bef9f2b6
Commit
00af00dfe8
@ -13,8 +13,8 @@ public class AltAuthBukkit extends JavaPlugin {
|
|||||||
@Setter
|
@Setter
|
||||||
private static JavaPlugin instance;
|
private static JavaPlugin instance;
|
||||||
|
|
||||||
private SessionServiceInjector serviceInjector;
|
private ServerIdInjector serverIdInjector;
|
||||||
private EncryptionRequestInjector requestInjector;
|
private AltAuthSessionService serviceInjector;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
@ -26,15 +26,13 @@ public class AltAuthBukkit extends JavaPlugin {
|
|||||||
saveDefaultConfig();
|
saveDefaultConfig();
|
||||||
String altAuthServer = getConfig().getString("altauth-proxy");
|
String altAuthServer = getConfig().getString("altauth-proxy");
|
||||||
|
|
||||||
ProtocolInjector.init();
|
serverIdInjector = new ServerIdInjector(this, altAuthServer);
|
||||||
serviceInjector = new SessionServiceInjector(altAuthServer);
|
serviceInjector = new AltAuthSessionService(serverIdInjector, altAuthServer);
|
||||||
requestInjector = new EncryptionRequestInjector(altAuthServer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
requestInjector.remove();
|
|
||||||
serviceInjector.revert();
|
serviceInjector.revert();
|
||||||
ProtocolInjector.instance.close();
|
serverIdInjector.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
146
bukkit/src/de/lixfel/altauth/bukkit/AltAuthSessionService.java
Normale Datei
146
bukkit/src/de/lixfel/altauth/bukkit/AltAuthSessionService.java
Normale Datei
@ -0,0 +1,146 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package de.lixfel.altauth.bukkit;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.authlib.GameProfileRepository;
|
||||||
|
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||||
|
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||||
|
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
|
||||||
|
import de.lixfel.ReflectionUtil;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class AltAuthSessionService implements MinecraftSessionService {
|
||||||
|
|
||||||
|
private static final ReflectionUtil.FieldWrapper<URL> CHECK_URL = ReflectionUtil.getField(YggdrasilMinecraftSessionService.class, URL.class, 1);
|
||||||
|
private static final boolean URL_STATIC = ReflectionUtil.MINECRAFT_VERSION < 17; //TODO maybe 16 (untested)
|
||||||
|
|
||||||
|
private static final Function<AltAuthSessionService, YggdrasilMinecraftSessionService> swapService;
|
||||||
|
private static final Consumer<AltAuthSessionService> revertService;
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (ReflectionUtil.MINECRAFT_VERSION < 19) {
|
||||||
|
ReflectionUtil.FieldWrapper<MinecraftSessionService> getService = ReflectionUtil.getField(ServerIdInjector.minecraftServer, MinecraftSessionService.class, 0);
|
||||||
|
|
||||||
|
swapService = altAuthService -> {
|
||||||
|
Object minecraftServer = altAuthService.serverIdInjector.minecraftServerInstance();
|
||||||
|
YggdrasilMinecraftSessionService service = (YggdrasilMinecraftSessionService) getService.get(minecraftServer);
|
||||||
|
getService.set(minecraftServer, altAuthService);
|
||||||
|
return service;
|
||||||
|
};
|
||||||
|
revertService = altAuthService -> getService.set(altAuthService.serverIdInjector.minecraftServerInstance(), altAuthService.service);
|
||||||
|
} else {
|
||||||
|
Class<?> services = ReflectionUtil.getClass("net.minecraft.server.Services");
|
||||||
|
ReflectionUtil.FieldWrapper<?> getServices = ReflectionUtil.getField(ServerIdInjector.minecraftServer, services, 0);
|
||||||
|
ReflectionUtil.FieldWrapper<MinecraftSessionService> getSessionService = ReflectionUtil.getField(services, MinecraftSessionService.class, 0);
|
||||||
|
Class<?> signatureValidator = ReflectionUtil.getClass("net.minecraft.util.SignatureValidator");
|
||||||
|
ReflectionUtil.FieldWrapper<?> getSignatureValidator = ReflectionUtil.getField(services, signatureValidator, 0);
|
||||||
|
ReflectionUtil.FieldWrapper<GameProfileRepository> getGameProfileRepository = ReflectionUtil.getField(services, GameProfileRepository.class, 0);
|
||||||
|
Class<?> userCache = ReflectionUtil.getClass("net.minecraft.server.players.UserCache");
|
||||||
|
ReflectionUtil.FieldWrapper<?> getUserCache = ReflectionUtil.getField(services, userCache, 0);
|
||||||
|
|
||||||
|
Constructor<?> constructor;
|
||||||
|
try {
|
||||||
|
constructor = services.getConstructor(MinecraftSessionService.class, signatureValidator, GameProfileRepository.class, userCache);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
swapService = altAuthService -> {
|
||||||
|
Object servicesInstance = getServices.get(altAuthService.serverIdInjector.minecraftServerInstance());
|
||||||
|
|
||||||
|
try {
|
||||||
|
getServices.set(
|
||||||
|
altAuthService.serverIdInjector.minecraftServerInstance(),
|
||||||
|
constructor.newInstance(altAuthService, getSignatureValidator.get(servicesInstance), getGameProfileRepository.get(servicesInstance), getUserCache.get(servicesInstance))
|
||||||
|
);
|
||||||
|
} catch (InstantiationException | IllegalAccessException| InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (YggdrasilMinecraftSessionService) getSessionService.get(servicesInstance);
|
||||||
|
};
|
||||||
|
revertService = altAuthService -> {
|
||||||
|
Object servicesInstance = getServices.get(altAuthService.serverIdInjector.minecraftServerInstance());
|
||||||
|
|
||||||
|
try {
|
||||||
|
getServices.set(
|
||||||
|
altAuthService.serverIdInjector.minecraftServerInstance(),
|
||||||
|
constructor.newInstance(altAuthService.service, getSignatureValidator.get(servicesInstance), getGameProfileRepository.get(servicesInstance), getUserCache.get(servicesInstance))
|
||||||
|
);
|
||||||
|
} catch (InstantiationException | IllegalAccessException| InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Class<?> minecraftEncryption = ReflectionUtil.getClass("net.minecraft.util.MinecraftEncryption");
|
||||||
|
private static final ReflectionUtil.MethodWrapper getServerHash = ReflectionUtil.getMethod(minecraftEncryption, byte[].class, null, String.class, PublicKey.class, SecretKey.class);
|
||||||
|
private static final ReflectionUtil.MethodWrapper getSecretKey = ReflectionUtil.getMethod(minecraftEncryption, null, PrivateKey.class, byte[].class);
|
||||||
|
|
||||||
|
private static final ReflectionUtil.FieldWrapper<KeyPair> getKeyPair = ReflectionUtil.getField(ServerIdInjector.minecraftServer, KeyPair.class, 0);
|
||||||
|
|
||||||
|
private final YggdrasilMinecraftSessionService service;
|
||||||
|
private final URL checkUrlBackup;
|
||||||
|
|
||||||
|
private final KeyPair keyPair;
|
||||||
|
private final ServerIdInjector serverIdInjector;
|
||||||
|
private final String altAuthServer;
|
||||||
|
|
||||||
|
public AltAuthSessionService(ServerIdInjector serverIdInjector, String altAuthServer) {
|
||||||
|
this.serverIdInjector = serverIdInjector;
|
||||||
|
this.altAuthServer = altAuthServer;
|
||||||
|
this.keyPair = getKeyPair.get(serverIdInjector.minecraftServerInstance());
|
||||||
|
|
||||||
|
service = swapService.apply(this);
|
||||||
|
checkUrlBackup = CHECK_URL.get(URL_STATIC ? null : service);
|
||||||
|
|
||||||
|
try {
|
||||||
|
CHECK_URL.set(URL_STATIC ? null : service, new URL("https://" + altAuthServer + "/session/minecraft/hasJoined"));
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
AltAuthBukkit.getInstance().getLogger().log(Level.SEVERE, "Could not create AltAuth URLs", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void revert() {
|
||||||
|
CHECK_URL.set(URL_STATIC ? null : service, checkUrlBackup);
|
||||||
|
revertService.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void joinServer(GameProfile gameProfile, String s, String s1) throws AuthenticationException {
|
||||||
|
service.joinServer(gameProfile, s, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameProfile hasJoinedServer(GameProfile gameProfile, String serverId, InetAddress inetAddress) throws AuthenticationUnavailableException {
|
||||||
|
return service.hasJoinedServer(gameProfile, new BigInteger((byte[]) getServerHash.invoke(null, altAuthServer, keyPair.getPublic(), getSecretKey.invoke(null, keyPair.getPrivate(), serverIdInjector.getEncryptedSecret(gameProfile.getName())))).toString(16), inetAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile gameProfile, boolean b) {
|
||||||
|
return service.getTextures(gameProfile, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameProfile fillProfileProperties(GameProfile gameProfile, boolean b) {
|
||||||
|
return service.fillProfileProperties(gameProfile, b);
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package de.lixfel.altauth.bukkit;
|
|
||||||
|
|
||||||
import de.lixfel.ReflectionUtil;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
public class EncryptionRequestInjector {
|
|
||||||
|
|
||||||
private static final Class<?> EncryptionRequest = ReflectionUtil.getClass("net.minecraft.network.protocol.login.PacketLoginOutEncryptionBegin");
|
|
||||||
private static final ReflectionUtil.FieldWrapper<String> ServerID = ReflectionUtil.getField(EncryptionRequest, String.class, 0);
|
|
||||||
|
|
||||||
private final String altAuthServer;
|
|
||||||
|
|
||||||
public EncryptionRequestInjector(String altAuthServer) {
|
|
||||||
this.altAuthServer = altAuthServer;
|
|
||||||
|
|
||||||
ProtocolInjector.instance.addFilter(EncryptionRequest, this::handleEncryptionRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object handleEncryptionRequest(Player player, Object packet) {
|
|
||||||
ServerID.set(packet, altAuthServer);
|
|
||||||
ProtocolInjector.instance.getInterceptor(player).ifPresent(ProtocolInjector.PacketInterceptor::close);
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
ProtocolInjector.instance.removeFilter(EncryptionRequest, this::handleEncryptionRequest);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,199 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package de.lixfel.altauth.bukkit;
|
|
||||||
|
|
||||||
import de.lixfel.ReflectionUtil;
|
|
||||||
import de.lixfel.ReflectionUtil.FieldWrapper;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelDuplexHandler;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.PlayerLoginEvent;
|
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
|
||||||
import org.bukkit.event.server.PluginDisableEvent;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
// ProtocolInjector heavily inspired by TinyProtocol
|
|
||||||
public class ProtocolInjector implements Listener {
|
|
||||||
|
|
||||||
private static final Class<?> craftServer = ReflectionUtil.getClass("org.bukkit.craftbukkit.CraftServer");
|
|
||||||
private static final Class<?> dedicatedPlayerList = ReflectionUtil.getClass("net.minecraft.server.dedicated.DedicatedPlayerList");
|
|
||||||
private static final FieldWrapper<?> getPlayerList = ReflectionUtil.getField(craftServer, dedicatedPlayerList, 0);
|
|
||||||
private static final Class<?> playerList = ReflectionUtil.getClass("net.minecraft.server.players.PlayerList");
|
|
||||||
public static final Class<?> minecraftServer = ReflectionUtil.getClass("net.minecraft.server.MinecraftServer");
|
|
||||||
private static final FieldWrapper<?> getMinecraftServer = ReflectionUtil.getField(playerList, minecraftServer, 0);
|
|
||||||
private static final Class<?> serverConnection = ReflectionUtil.getClass("net.minecraft.server.network.ServerConnection");
|
|
||||||
private static final FieldWrapper<?> getServerConnection = ReflectionUtil.getField(minecraftServer, serverConnection, 0);
|
|
||||||
private static final Class<?> networkManager = ReflectionUtil.getClass("net.minecraft.network.NetworkManager");
|
|
||||||
private static final FieldWrapper<List> getConnections = ReflectionUtil.getField(serverConnection, List.class, 0, networkManager);
|
|
||||||
|
|
||||||
public static final ProtocolInjector instance = new ProtocolInjector(AltAuthBukkit.getInstance());
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
//enforce init
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Plugin plugin;
|
|
||||||
private final String handlerName;
|
|
||||||
private final Object minecraftServerInstance;
|
|
||||||
private final List<?> connections;
|
|
||||||
private boolean closed;
|
|
||||||
|
|
||||||
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>();
|
|
||||||
private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>();
|
|
||||||
|
|
||||||
private ProtocolInjector(final Plugin plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.handlerName = "altauth";
|
|
||||||
this.minecraftServerInstance = getMinecraftServer.get(getPlayerList.get(plugin.getServer()));
|
|
||||||
this.connections = getConnections.get(getServerConnection.get(minecraftServerInstance));
|
|
||||||
|
|
||||||
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
|
||||||
|
|
||||||
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
|
||||||
new PacketInterceptor(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object minecraftServer() {
|
|
||||||
return minecraftServerInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
|
||||||
public void onPlayerLogin(PlayerLoginEvent e) {
|
|
||||||
if(closed)
|
|
||||||
return;
|
|
||||||
new PacketInterceptor(e.getPlayer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR)
|
|
||||||
public void onPlayerDisconnect(PlayerQuitEvent e) {
|
|
||||||
getInterceptor(e.getPlayer()).ifPresent(PacketInterceptor::close);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginDisable(PluginDisableEvent e) {
|
|
||||||
if (e.getPlugin().equals(plugin)) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addFilter(Class<?> packetType, BiFunction<Player, Object, Object> filter) {
|
|
||||||
packetFilters.computeIfAbsent(packetType, c -> new ArrayList<>(1)).add(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeFilter(Class<?> packetType, BiFunction<Player, Object, Object> filter) {
|
|
||||||
packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void close() {
|
|
||||||
if(closed)
|
|
||||||
return;
|
|
||||||
closed = true;
|
|
||||||
|
|
||||||
HandlerList.unregisterAll(this);
|
|
||||||
|
|
||||||
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
|
||||||
getInterceptor(player).ifPresent(PacketInterceptor::close);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<PacketInterceptor> getInterceptor(Player player) {
|
|
||||||
synchronized (playerInterceptors) {
|
|
||||||
return Optional.ofNullable(playerInterceptors.get(player));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final FieldWrapper<Channel> getChannel = ReflectionUtil.getField(networkManager, Channel.class, 0);
|
|
||||||
private static final FieldWrapper<UUID> getUUID = ReflectionUtil.getField(networkManager, UUID.class, 0);
|
|
||||||
|
|
||||||
public final class PacketInterceptor extends ChannelDuplexHandler {
|
|
||||||
private final Player player;
|
|
||||||
private final Channel channel;
|
|
||||||
|
|
||||||
private PacketInterceptor(Player player) {
|
|
||||||
this.player = player;
|
|
||||||
|
|
||||||
channel = getChannel.get(connections.stream().filter(connection -> player.getUniqueId().equals(getUUID.get(connection))).findAny().orElseThrow(() -> new SecurityException("Could not find channel for player " + player.getName())));
|
|
||||||
|
|
||||||
synchronized (playerInterceptors) {
|
|
||||||
playerInterceptors.put(player, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.pipeline().addBefore("packet_handler", handlerName, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendPacket(Object packet) {
|
|
||||||
channel.pipeline().writeAndFlush(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void receivePacket(Object packet) {
|
|
||||||
channel.pipeline().context("encoder").fireChannelRead(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
if(channel.isActive()) {
|
|
||||||
channel.eventLoop().execute(() -> {
|
|
||||||
try {
|
|
||||||
channel.pipeline().remove(handlerName);
|
|
||||||
} catch (NoSuchElementException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (playerInterceptors) {
|
|
||||||
playerInterceptors.remove(player, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
|
||||||
try {
|
|
||||||
msg = filterPacket(player, msg);
|
|
||||||
} catch (Exception e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg != null) {
|
|
||||||
super.channelRead(ctx, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
|
||||||
try {
|
|
||||||
msg = filterPacket(player, msg);
|
|
||||||
} catch (Exception e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg != null) {
|
|
||||||
super.write(ctx, msg, promise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object filterPacket(Player player, Object packet) {
|
|
||||||
List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList());
|
|
||||||
|
|
||||||
for(BiFunction<Player, Object, Object> filter : filters) {
|
|
||||||
packet = filter.apply(player, packet);
|
|
||||||
|
|
||||||
if(packet == null)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
126
bukkit/src/de/lixfel/altauth/bukkit/ServerIdInjector.java
Normale Datei
126
bukkit/src/de/lixfel/altauth/bukkit/ServerIdInjector.java
Normale Datei
@ -0,0 +1,126 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package de.lixfel.altauth.bukkit;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import de.lixfel.ReflectionUtil;
|
||||||
|
import de.lixfel.ReflectionUtil.FieldWrapper;
|
||||||
|
import io.netty.channel.*;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
// ServerIdInjector heavily inspired by TinyProtocol
|
||||||
|
public class ServerIdInjector {
|
||||||
|
|
||||||
|
private static final Class<?> craftServer = ReflectionUtil.getClass("org.bukkit.craftbukkit.CraftServer");
|
||||||
|
private static final Class<?> dedicatedPlayerList = ReflectionUtil.getClass("net.minecraft.server.dedicated.DedicatedPlayerList");
|
||||||
|
private static final FieldWrapper<?> getPlayerList = ReflectionUtil.getField(craftServer, dedicatedPlayerList, 0);
|
||||||
|
private static final Class<?> playerList = ReflectionUtil.getClass("net.minecraft.server.players.PlayerList");
|
||||||
|
public static final Class<?> minecraftServer = ReflectionUtil.getClass("net.minecraft.server.MinecraftServer");
|
||||||
|
private static final FieldWrapper<?> getMinecraftServer = ReflectionUtil.getField(playerList, minecraftServer, 0);
|
||||||
|
private static final Class<?> serverConnection = ReflectionUtil.getClass("net.minecraft.server.network.ServerConnection");
|
||||||
|
private static final FieldWrapper<?> getServerConnection = ReflectionUtil.getField(minecraftServer, serverConnection, 0);
|
||||||
|
private static final FieldWrapper<List> getChannelFutures = ReflectionUtil.getField(serverConnection, List.class, 0, ChannelFuture.class);
|
||||||
|
|
||||||
|
private static final Class networkManager = ReflectionUtil.getClass("net.minecraft.network.NetworkManager");
|
||||||
|
private static final Class<?> packetListener = ReflectionUtil.getClass("net.minecraft.network.PacketListener");
|
||||||
|
private static final FieldWrapper<?> getPacketListener = ReflectionUtil.getField(networkManager, packetListener, 0);
|
||||||
|
|
||||||
|
public static final Class<?> loginListener = ReflectionUtil.getClass("net.minecraft.server.network.LoginListener");
|
||||||
|
private static final FieldWrapper<GameProfile> getGameProfile = ReflectionUtil.getField(loginListener, GameProfile.class, 0);
|
||||||
|
|
||||||
|
private static final Class<?> packetLoginInEncryptionBegin = ReflectionUtil.getClass("net.minecraft.network.protocol.login.PacketLoginInEncryptionBegin");
|
||||||
|
private static final FieldWrapper<byte[]> getEncryptedSecret = ReflectionUtil.getField(packetLoginInEncryptionBegin, byte[].class, 0);
|
||||||
|
private static final Class<?> packetLoginOutEncryptionBegin = ReflectionUtil.getClass("net.minecraft.network.protocol.login.PacketLoginOutEncryptionBegin");
|
||||||
|
private static final FieldWrapper<String> getPacketServerID = ReflectionUtil.getField(packetLoginOutEncryptionBegin, String.class, 0);
|
||||||
|
|
||||||
|
private final String altAuthServer;
|
||||||
|
private final String handlerName;
|
||||||
|
private final String initializerName;
|
||||||
|
private final Object minecraftServerInstance;
|
||||||
|
private final List<ChannelFuture> channelFutures;
|
||||||
|
|
||||||
|
private final Map<String, byte[]> encryptedSecrets = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
|
private final ChannelInitializer<Channel> initializer = new ChannelInitializer<Channel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel channel) {
|
||||||
|
if(!closed)
|
||||||
|
new PacketInterceptor(channel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public ServerIdInjector(Plugin plugin, String altAuthServer) {
|
||||||
|
this.altAuthServer = altAuthServer;
|
||||||
|
this.handlerName = "altauth";
|
||||||
|
this.initializerName = "altauth-init";
|
||||||
|
this.minecraftServerInstance = getMinecraftServer.get(getPlayerList.get(plugin.getServer()));
|
||||||
|
this.channelFutures = getChannelFutures.get(getServerConnection.get(minecraftServerInstance));
|
||||||
|
|
||||||
|
for(ChannelFuture serverChannel : channelFutures) {
|
||||||
|
serverChannel.channel().pipeline().addFirst(initializerName, new ChannelInboundHandlerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object o) throws Exception {
|
||||||
|
super.channelRead(ctx, o);
|
||||||
|
|
||||||
|
Channel channel = (Channel) o;
|
||||||
|
channel.pipeline().addLast(initializerName, initializer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object minecraftServerInstance() {
|
||||||
|
return minecraftServerInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getEncryptedSecret(String name) {
|
||||||
|
return encryptedSecrets.remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void close() {
|
||||||
|
if(closed)
|
||||||
|
return;
|
||||||
|
closed = true;
|
||||||
|
|
||||||
|
for(ChannelFuture serverChannel : channelFutures) {
|
||||||
|
serverChannel.channel().pipeline().remove(initializerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class PacketInterceptor extends ChannelDuplexHandler {
|
||||||
|
private final Channel channel;
|
||||||
|
|
||||||
|
private PacketInterceptor(Channel channel) {
|
||||||
|
this.channel = channel;
|
||||||
|
channel.pipeline().addBefore("packet_handler", handlerName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
|
||||||
|
if(packetLoginInEncryptionBegin.isInstance(packet)) {
|
||||||
|
encryptedSecrets.put(
|
||||||
|
getGameProfile.get(getPacketListener.get(channel.pipeline().get(networkManager))).getName(),
|
||||||
|
getEncryptedSecret.get(packet)
|
||||||
|
);
|
||||||
|
channel.pipeline().remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.channelRead(ctx, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
|
||||||
|
if(packetLoginOutEncryptionBegin.isInstance(packet)) {
|
||||||
|
getPacketServerID.set(packet, altAuthServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.write(ctx, packet, promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package de.lixfel.altauth.bukkit;
|
|
||||||
|
|
||||||
import de.lixfel.ReflectionUtil;
|
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class SessionServiceInjector {
|
|
||||||
|
|
||||||
private static final Class<?> MINECRAFT_SESSION_SERVICE = ReflectionUtil.getClass("com.mojang.authlib.minecraft.MinecraftSessionService");
|
|
||||||
private static final Class<?> YGGDRASIL_SESSION_SERVICE = ReflectionUtil.getClass("com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService");
|
|
||||||
|
|
||||||
private static final ReflectionUtil.FieldWrapper<URL> JOIN_URL = ReflectionUtil.getField(YGGDRASIL_SESSION_SERVICE, URL.class, 0);
|
|
||||||
private static final ReflectionUtil.FieldWrapper<URL> CHECK_URL = ReflectionUtil.getField(YGGDRASIL_SESSION_SERVICE, URL.class, 1);
|
|
||||||
|
|
||||||
private final Object sessionService;
|
|
||||||
private final URL joinUrlBackup;
|
|
||||||
private final URL checkUrlBackup;
|
|
||||||
|
|
||||||
public SessionServiceInjector(String altAuthServer) {
|
|
||||||
altAuthServer = "https://" + altAuthServer + "/session/minecraft/";
|
|
||||||
|
|
||||||
if(ReflectionUtil.MINECRAFT_VERSION < 17) { //TODO maybe 16 (untested)
|
|
||||||
sessionService = null;
|
|
||||||
} else if (ReflectionUtil.MINECRAFT_VERSION < 19) {
|
|
||||||
sessionService = ReflectionUtil.getField(ProtocolInjector.minecraftServer, MINECRAFT_SESSION_SERVICE, 0).get(
|
|
||||||
ProtocolInjector.instance.minecraftServer()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
Class<?> services = ReflectionUtil.getClass("net.minecraft.server.Services");
|
|
||||||
sessionService = ReflectionUtil.getField(services, MINECRAFT_SESSION_SERVICE, 0).get(
|
|
||||||
ReflectionUtil.getField(ProtocolInjector.minecraftServer, services, 0).get(
|
|
||||||
ProtocolInjector.instance.minecraftServer()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
joinUrlBackup = JOIN_URL.get(sessionService);
|
|
||||||
checkUrlBackup = CHECK_URL.get(sessionService);
|
|
||||||
|
|
||||||
try {
|
|
||||||
JOIN_URL.set(sessionService, new URL(altAuthServer + "join"));
|
|
||||||
CHECK_URL.set(sessionService, new URL(altAuthServer + "hasJoined"));
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
AltAuthBukkit.getInstance().getLogger().log(Level.SEVERE, "Could not create AltAuth URLs", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void revert() {
|
|
||||||
JOIN_URL.set(sessionService, joinUrlBackup);
|
|
||||||
CHECK_URL.set(sessionService, checkUrlBackup);
|
|
||||||
}
|
|
||||||
}
|
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren