1.15.2 [ProtocolLib] How to complete the login packet sequece

Discussion in 'Spigot Plugin Development' started by tcklpl, Feb 5, 2020.

  1. I'm trying to manually authenticate players that join the server to be able to make players that login through a premium account bypass some things in my server. I was able to get the server to send the EncryptionBegin package, listen for its response and get the server hash. When I manually accessed Mojang session server with that information I was able to retrieve what I need. However the login fails, what do I need to do to complete the login and make the player join the server?

    Please ignore the horrible structure of this code, I'm just testing, when I get it to work I'll structure it better
    Code (Java):
                    new PacketAdapter(this, PacketType.Login.Client.START) {
                        public void onPacketReceiving(PacketEvent event) {
                            getLogger().info(ChatColor.YELLOW + "Recieving packet");
                            PacketContainer container = new PacketContainer(PacketType.Login.Server.ENCRYPTION_BEGIN);
                            container.getStrings().write(0, "");
                            container.getSpecificModifier(PublicKey.class).write(0, ((CraftServer) getServer()).getServer().getKeyPair().getPublic());
                            byte[] data = new byte[4];
                            new Random().nextBytes(data);
                            container.getByteArrays().write(0, data);
                            try {
                                ProtocolLibrary.getProtocolManager().sendServerPacket(event.getPlayer(), container);
                            } catch (InvocationTargetException e) {

                    new PacketAdapter(this, PacketType.Login.Client.ENCRYPTION_BEGIN) {
                        public void onPacketReceiving(PacketEvent event) {
                            getLogger().info(ChatColor.YELLOW + "Recieving packet");

                            BigInteger test = new BigInteger(MinecraftEncryption.a("",
                            getLogger().info(ChatColor.GREEN + test.toString(16));

    The console output:
    Code (Text):
    [20:46:04 INFO]: [Natural Disasters] Recieving packet
    [20:46:04 INFO]: UUID of player tcklpl is 1374fa5d-fb74-3db1-bafe-3c0832938d0d
    [20:46:04 INFO]: [Natural Disasters] Recieving packet
    [20:46:04 INFO]: [Natural Disasters] -1634353e3f971db4f74eab2b62d03d20323fe65b
    [20:46:05 INFO]: tcklpl lost connection: Internal Exception: java.lang.IllegalStateException: Unexpected key packet
    The first time I try to login the client just says Disconnected, the next times it show a lot of different errors: Packet larger than expected, Packed below threshold, Index out of bounds, DataFormatException and a lot of others.
    #1 tcklpl, Feb 5, 2020
    Last edited: Feb 5, 2020
  2. Im not sure spigot supports offline-mode... you might be out of luck on this one,also this question should be forwarded to the actual dev of ProtocolLib as he is more likely to give you an answer
  3. Okay, I was able to do some things, first I created a listener for every login packet sent and received, what I was able to get was the following sequence of packets:

    When running on offline mode:
    Code (Text):
    [11:50:03 INFO]: [Natural Disasters] Receiving packet LOGIN START
    [11:50:03 INFO]: UUID of player tcklpl is 1374fa5d-fb74-3db1-bafe-3c0832938d0d
    [11:50:04 INFO]: [Natural Disasters] Sending packet SET COMPRESSION
    [11:50:04 INFO]: [Natural Disasters] Sending packet SUCCESS
    [11:50:04 INFO]: tcklpl[/] logged in with entity id 2 at ([void]26.43719040806936, 5.79341791522513, -34.043970669361016)
    When running on online mode:
    Code (Text):
    [11:51:58 INFO]: [Natural Disasters] Receiving packet LOGIN START
    [11:51:58 INFO]: [Natural Disasters] Sending packet ENCRYPTION BEGINS
    [11:51:58 INFO]: [Natural Disasters] Receiving packet ENCRYPTION BEGINS
    [11:51:59 INFO]: UUID of player tcklpl is 4d92efb4-481e-4a95-b155-1f565b422d23
    [11:51:59 INFO]: [Natural Disasters] Sending packet SET COMPRESSION
    [11:51:59 INFO]: [Natural Disasters] Sending packet SUCCESS
    [11:51:59 INFO]: tcklpl[/] logged in with entity id 2 at ([void]23.699999988079078, 14.0, 11.699999988079073)
    After a long time trying to make offline mode send and receive ENCRYPTION_BEGINS packets I chose to switch to online mode and intercept the packets before they reached the server, to this I'd need only to get the GameProfile (or it's name) from LOGIN_START to later authenticate using my methods during ENCRYPTION_BEGINS and blocking this packet from reaching the server, otherwise offline players would be kicked.

    To achieve this I just created a HashMap<INetSocketAddress, GameProfile> and stored the values there during LOGIN_START and retrieved them during ENCRYPTION_BEGINS using player#getAddress(). (Notice 'player' here is of type TemporaryPlayer and doesn't have all player info and functions, this info would only be created after the login).

    The authentication is working correctly, if it gets a response from the Mojang Session Server it will retrieve both UUID and skin data and use it to the complete GameProfile, otherwise it will just create an OfflineMode UUID according to what vanilla does.

    The problem is: after said GameProfile is generated I can't just send it to the player through LOGIN_SUCCESS packet, the server needs to register the player. I was digging through NMS and found the LoginListener class wich supposedly handles this, in said class there is the method c():
    Code (Java):
    public void c() {
        EntityPlayer s = this.server.getPlayerList().attemptLogin(this, this.i, this.hostname);
        if (s != null) {
          this.g = EnumProtocolState.ACCEPTED;
          if (this.server.az() >= 0 && !this.networkManager.isLocal())
            this.networkManager.sendPacket(new PacketLoginOutSetCompression(this.server.az()), channelfuture -> this.networkManager.setCompressionLevel(this.server.az()));
          this.networkManager.sendPacket(new PacketLoginOutSuccess(this.i));
          EntityPlayer entityplayer = this.server.getPlayerList().a(this.i.getId());
          if (entityplayer != null) {
            this.g = EnumProtocolState.DELAY_ACCEPT;
            this.l = this.server.getPlayerList().processLogin(this.i, s);
          } else {
            this.server.getPlayerList().a(this.networkManager, this.server.getPlayerList().processLogin(this.i, s));
    on server#getPlayerList()#attemptLogin()
    • this is the LoginListener;
    • this.i is a GameProfile; and
    • this.hostname idk, probably an empty string.
    What I'd like to know is how can I get the instance of LoginListener used on the current login, or even if I could instance my own for this purpose because attemptLogin() only uses it to get the NetworkManager and to disconnect the login attempt. (The problem in instantiating my own LoginListener is that in the constructor it needs the MinecraftServer (ok) and the NetworkManager (that I can't get because I can't cast player to CraftPlayer due to it being a TemporaryPlayer)
  4. Just pointing out the fact that this is clearly for a cracked server and spigot doesn't support that.
    Sorry but you're out of luck.