Deal properly with partially received packets

This commit is contained in:
UnfamiliarLegacy 2024-06-28 04:52:20 +02:00
parent f5a88398c5
commit b6d5875f5f
3 changed files with 53 additions and 5 deletions

View File

@ -121,6 +121,8 @@ public class Rc4Obtainer {
} }
private boolean onSendFirstEncryptedMessage(EncryptedPacketHandler packetHandler, List<byte[]> potentialRC4tables) { private boolean onSendFirstEncryptedMessage(EncryptedPacketHandler packetHandler, List<byte[]> potentialRC4tables) {
logger.info("Attempting to brute force RC4 table");
if (potentialRC4tables == null || potentialRC4tables.isEmpty()) { if (potentialRC4tables == null || potentialRC4tables.isEmpty()) {
return false; return false;
} }

View File

@ -17,6 +17,7 @@ public class ShockwaveOutBuffer extends PayloadBuffer {
public static final int PACKET_SIZE_MIN = PACKET_HEADER_SIZE + PACKET_LENGTH_SIZE; public static final int PACKET_SIZE_MIN = PACKET_HEADER_SIZE + PACKET_LENGTH_SIZE;
public static final int PACKET_SIZE_MIN_ENCRYPTED = PACKET_HEADER_SIZE + PACKET_LENGTH_SIZE_ENCRYPTED; public static final int PACKET_SIZE_MIN_ENCRYPTED = PACKET_HEADER_SIZE + PACKET_LENGTH_SIZE_ENCRYPTED;
private int previousLength = 0;
private RC4Cipher cipher; private RC4Cipher cipher;
@Override @Override
@ -39,12 +40,15 @@ public class ShockwaveOutBuffer extends PayloadBuffer {
int length; int length;
if (this.cipher != null) { if (this.cipher != null) {
final byte[] decData = this.cipher.decipher(buffer, 0, PACKET_LENGTH_SIZE_ENCRYPTED); if (previousLength == 0) {
final int decDataLen = Base64Encoding.decode(new byte[]{decData[1], decData[2], decData[3]}); final byte[] decData = this.cipher.decipher(buffer, 0, PACKET_LENGTH_SIZE_ENCRYPTED);
// TODO: Store length in a variable for if we don't have enough bytes. // When a packet has been received that we can't fully read, we need to store the decrypted length.
// Otherwise, we would keep decrypting the same bytes and mutating the rc4 state, messing up the entire state.
length = decDataLen; length = previousLength = Base64Encoding.decode(new byte[]{decData[1], decData[2], decData[3]});
} else {
length = previousLength;
}
} else { } else {
length = Base64Encoding.decode(new byte[]{buffer[0], buffer[1], buffer[2]}); length = Base64Encoding.decode(new byte[]{buffer[0], buffer[1], buffer[2]});
} }
@ -58,6 +62,7 @@ public class ShockwaveOutBuffer extends PayloadBuffer {
out.add(Arrays.copyOfRange(buffer, packetLengthSize, endPos)); out.add(Arrays.copyOfRange(buffer, packetLengthSize, endPos));
buffer = Arrays.copyOfRange(buffer, endPos, buffer.length); buffer = Arrays.copyOfRange(buffer, endPos, buffer.length);
previousLength = 0;
} }
return out.toArray(new byte[0][]); return out.toArray(new byte[0][]);

View File

@ -7,6 +7,7 @@ import gearth.protocol.memory.habboclient.HabboClientFactory;
import gearth.protocol.memory.habboclient.external.MemoryClient; import gearth.protocol.memory.habboclient.external.MemoryClient;
import gearth.protocol.packethandler.EncryptedPacketHandler; import gearth.protocol.packethandler.EncryptedPacketHandler;
import gearth.protocol.packethandler.shockwave.ShockwavePacketOutgoingHandler; import gearth.protocol.packethandler.shockwave.ShockwavePacketOutgoingHandler;
import gearth.protocol.packethandler.shockwave.buffers.ShockwaveOutBuffer;
import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic; import org.mockito.MockedStatic;
@ -210,4 +211,44 @@ public class TestRc4Shockwave {
assertEquals(h3Q, cipher.getQ()); assertEquals(h3Q, cipher.getQ());
assertEquals(h3J, cipher.getJ()); assertEquals(h3J, cipher.getJ());
} }
@Test
public void testSplitPackets() {
final RC4Cipher c = new RC4Base64(
Hex.decode("d102ecab2e8d0a851000a393a483de68f2182f879b884bb6be77595701ffc0900db9f00f415332fd9fe35dcc8ceaa5c480214cc8ee661627e23736795b444a3d5a3f721cfbd4b370d7daaae7f8b1769a78569c3065d6c7607438752c39ced51992634e15a850efcd1a5fd0b782cb6254f4c598f36f485e35f7bf86a9fc03344d8ae9fe07db1355bc7d7f2de8e42052b424e0954f8f71df4612b0c95c7c252aacc394edbb7e45b540f6cadcf18443678ebd513169580e913a3b6b0664290c11fac173a26e1f09ada66199b2b805d8d9d38117331b0447262b1d8b08ddcf7b42e63e97289d23896d1ea70b147af5e19ed2aeebaf6c3cbac2a16a96e522c6f949a0"),
152,
79
);
final ShockwaveOutBuffer buffer = new ShockwaveOutBuffer();
buffer.setCipher(c);
// State.
int received = 0;
// Packet 1.
buffer.push(Hex.decode("4b78316b61774f426838576553476f48355478684946554c7368525749373856326a784b723762"));
received += buffer.receive().length;
buffer.push(Hex.decode("7866797279636c313743324a34714a392f5361584e656a6a6b6e53657550476b7638684637302b354d42484d373350434c434a3977"));
received += buffer.receive().length;
// Packet 2.
buffer.push(Hex.decode("3634616845413754796c6371324d346c"));
received += buffer.receive().length;
buffer.push(Hex.decode("546f616b62484d55346a5943694c4b386c4a"));
received += buffer.receive().length;
// Packet 3.
buffer.push(Hex.decode("4750"));
received += buffer.receive().length;
buffer.push(Hex.decode("41364d67417330"));
received += buffer.receive().length;
assertEquals(3, received);
assertTrue(buffer.isEmpty());
}
} }