Updated G-MemZ, added universal macOS binary, improved flash rc4 crack

This commit is contained in:
UnfamiliarLegacy 2024-06-28 04:30:56 +02:00
parent 2087932a5e
commit f5a88398c5
15 changed files with 88 additions and 439 deletions

View File

@ -76,9 +76,7 @@ public class Rc4Obtainer {
@Override @Override
public void onPacket() { public void onPacket() {
if (handler.isEncryptedStream()) { if (handler.isEncryptedStream()) {
final boolean isShockwave = handler instanceof ShockwavePacketOutgoingHandler; if (counter.incrementAndGet() != 3) {
if (isShockwave && counter.incrementAndGet() != 3) {
return; return;
} }
@ -99,25 +97,11 @@ public class Rc4Obtainer {
logger.info("Caught encrypted packet, attempting to find decryption keys"); logger.info("Caught encrypted packet, attempting to find decryption keys");
final HabboClient client = HabboClientFactory.get(hConnection); final HabboClient client = HabboClientFactory.get(hConnection);
if (client == null) {
logger.info("Unsupported platform / client combination, aborting connection");
hConnection.abort();
return;
}
new Thread(() -> { new Thread(() -> {
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
boolean worked = false; if (!onSendFirstEncryptedMessage(flashPacketHandler, client.getRC4Tables())) {
int i = 0;
while (!worked && i < 4) {
worked = (i % 2 == 0) ?
onSendFirstEncryptedMessage(flashPacketHandler, client.getRC4cached()) :
onSendFirstEncryptedMessage(flashPacketHandler, client.getRC4possibilities());
i++;
}
if (!worked) {
try { try {
Platform.runLater(Rc4Obtainer::showErrorDialog); Platform.runLater(Rc4Obtainer::showErrorDialog);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
@ -153,8 +137,16 @@ public class Rc4Obtainer {
} }
if (packetHandler instanceof FlashPacketHandler) { if (packetHandler instanceof FlashPacketHandler) {
// Fast-path.
for (byte[] possible : potentialRC4tables) { for (byte[] possible : potentialRC4tables) {
if (bruteFlash(packetHandler, encBuffer, possible)) { if (bruteFlashFast(packetHandler, encBuffer, possible)) {
return true;
}
}
// Slow-path.
for (byte[] possible : potentialRC4tables) {
if (bruteFlashSlow(packetHandler, encBuffer, possible)) {
return true; return true;
} }
} }
@ -178,11 +170,13 @@ public class Rc4Obtainer {
} }
private boolean bruteShockwaveHeaderFast(EncryptedPacketHandler packetHandler, byte[] encBuffer, byte[] tableState) { private boolean bruteShockwaveHeaderFast(EncryptedPacketHandler packetHandler, byte[] encBuffer, byte[] tableState) {
final int HardcodedQ = 164; // Table state Q starts at 152 after premixing.
// Add 12 for the 3 headers being encrypted and you get 164.
final int EstimatedQ = 164;
for (int j = 0; j < 256; j++) { for (int j = 0; j < 256; j++) {
if (bruteShockwaveHeader(packetHandler, encBuffer, tableState, HardcodedQ, j)) { if (bruteShockwaveHeader(packetHandler, encBuffer, tableState, EstimatedQ, j)) {
logger.debug("Brute forced with fast path"); logger.debug("Brute forced shockwave with fast path");
return true; return true;
} }
} }
@ -194,7 +188,7 @@ public class Rc4Obtainer {
for (int q = 0; q < 256; q++) { for (int q = 0; q < 256; q++) {
for (int j = 0; j < 256; j++) { for (int j = 0; j < 256; j++) {
if (bruteShockwaveHeader(packetHandler, encBuffer, tableState, q, j)) { if (bruteShockwaveHeader(packetHandler, encBuffer, tableState, q, j)) {
logger.debug("Brute forced with slow path"); logger.debug("Brute forced shockwave with slow path");
return true; return true;
} }
} }
@ -237,20 +231,41 @@ public class Rc4Obtainer {
return false; return false;
} }
private boolean bruteFlash(EncryptedPacketHandler flashPacketHandler, byte[] encBuffer, byte[] possible) { private boolean bruteFlashFast(EncryptedPacketHandler packetHandler, byte[] encBuffer, byte[] tableState) {
final int EstimatedQ = encBuffer.length % 256;
try {
for (int i = 0; i < 256; i++) {
for (int j = 0; j < 256; j++) { for (int j = 0; j < 256; j++) {
if (bruteFlash(packetHandler, encBuffer, tableState, EstimatedQ, j)) {
logger.debug("Brute forced flash with fast path");
return true;
}
}
final byte[] keycpy = Arrays.copyOf(possible, possible.length); return false;
final RC4 rc4Tryout = new RC4(keycpy, i, j); }
if (flashPacketHandler.getDirection() == HMessage.Direction.TOSERVER) private boolean bruteFlashSlow(EncryptedPacketHandler packetHandler, byte[] encBuffer, byte[] tableState) {
for (int q = 0; q < 256; q++) {
for (int j = 0; j < 256; j++) {
if (bruteFlash(packetHandler, encBuffer, tableState, q, j)) {
logger.debug("Brute forced flash with slow path");
return true;
}
}
}
return false;
}
private boolean bruteFlash(EncryptedPacketHandler flashPacketHandler, byte[] encBuffer, byte[] tableState, int q, int j) {
final byte[] keycpy = Arrays.copyOf(tableState, tableState.length);
final RC4 rc4Tryout = new RC4(keycpy, q, j);
if (flashPacketHandler.getDirection() == HMessage.Direction.TOSERVER) {
rc4Tryout.undoRc4(encBuffer); rc4Tryout.undoRc4(encBuffer);
}
if (rc4Tryout.couldBeFresh()) { if (rc4Tryout.couldBeFresh()) {
final byte[] encDataCopy = Arrays.copyOf(encBuffer, encBuffer.length); final byte[] encDataCopy = Arrays.copyOf(encBuffer, encBuffer.length);
final RC4 rc4TryCopy = rc4Tryout.deepCopy(); final RC4 rc4TryCopy = rc4Tryout.deepCopy();
@ -266,14 +281,10 @@ public class Rc4Obtainer {
return true; return true;
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); // ignore
} }
} }
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false; return false;
} }
} }

View File

@ -1,22 +1,12 @@
package gearth.protocol.memory.habboclient; package gearth.protocol.memory.habboclient;
import gearth.protocol.HConnection;
import java.util.List; import java.util.List;
/** /**
* Created by Jonas on 13/06/18. * Created by Jonas on 13/06/18.
*/ */
public abstract class HabboClient { public interface HabboClient {
protected HConnection hConnection; List<byte[]> getRC4Tables();
public HabboClient(HConnection connection) {
this.hConnection = connection;
}
// optional
public abstract List<byte[]> getRC4cached();
public abstract List<byte[]> getRC4possibilities();
} }

View File

@ -2,11 +2,8 @@ package gearth.protocol.memory.habboclient;
import gearth.misc.OSValidator; import gearth.misc.OSValidator;
import gearth.protocol.HConnection; import gearth.protocol.HConnection;
import gearth.protocol.connection.HClient;
import gearth.protocol.memory.habboclient.linux.LinuxHabboClient; import gearth.protocol.memory.habboclient.linux.LinuxHabboClient;
import gearth.protocol.memory.habboclient.macOs.MacOsHabboClient; import gearth.protocol.memory.habboclient.external.MemoryClient;
import gearth.protocol.memory.habboclient.shockwave.ShockwaveMemoryClient;
import gearth.protocol.memory.habboclient.windows.WindowsHabboClient;
/** /**
* Created by Jonas on 13/06/18. * Created by Jonas on 13/06/18.
@ -14,17 +11,11 @@ import gearth.protocol.memory.habboclient.windows.WindowsHabboClient;
public class HabboClientFactory { public class HabboClientFactory {
public static HabboClient get(HConnection connection) { public static HabboClient get(HConnection connection) {
if (connection.getClientType() == HClient.SHOCKWAVE) { if (OSValidator.isUnix()) {
return new ShockwaveMemoryClient(connection); return new LinuxHabboClient(connection);
} else {
if (OSValidator.isWindows()) return new WindowsHabboClient(connection);
if (OSValidator.isUnix()) return new LinuxHabboClient(connection);
if (OSValidator.isMac()) return new MacOsHabboClient(connection);
} }
// todo use rust if beneficial return new MemoryClient(connection);
return null;
} }
} }

View File

@ -1,8 +1,9 @@
package gearth.protocol.memory.habboclient.shockwave; package gearth.protocol.memory.habboclient.external;
import gearth.encoding.HexEncoding; import gearth.encoding.HexEncoding;
import gearth.misc.OSValidator; import gearth.misc.OSValidator;
import gearth.protocol.HConnection; import gearth.protocol.HConnection;
import gearth.protocol.connection.HClient;
import gearth.protocol.memory.habboclient.HabboClient; import gearth.protocol.memory.habboclient.HabboClient;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -17,21 +18,18 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
public class ShockwaveMemoryClient extends HabboClient { public class MemoryClient implements HabboClient {
private static final Logger logger = LoggerFactory.getLogger(ShockwaveMemoryClient.class); private static final Logger logger = LoggerFactory.getLogger(MemoryClient.class);
public ShockwaveMemoryClient(HConnection connection) { private final HConnection connection;
super(connection);
public MemoryClient(HConnection connection) {
this.connection = connection;
} }
@Override @Override
public List<byte[]> getRC4cached() { public List<byte[]> getRC4Tables() {
return Collections.emptyList();
}
@Override
public List<byte[]> getRC4possibilities() {
final List<byte[]> result = new ArrayList<>(); final List<byte[]> result = new ArrayList<>();
try { try {
@ -41,7 +39,7 @@ public class ShockwaveMemoryClient extends HabboClient {
result.add(HexEncoding.toBytes(potentialTable)); result.add(HexEncoding.toBytes(potentialTable));
} }
} catch (IOException | URISyntaxException e) { } catch (IOException | URISyntaxException e) {
logger.error("Failed to read RC4 possibilities from the Shockwave client", e); logger.error("Failed to read RC4 possibilities from the client", e);
} }
// Reverse the list so that the most likely keys are at the top. // Reverse the list so that the most likely keys are at the top.
@ -59,7 +57,8 @@ public class ShockwaveMemoryClient extends HabboClient {
filePath += "/G-MemZ"; filePath += "/G-MemZ";
} }
final ProcessBuilder pb = new ProcessBuilder(filePath); final String hotelType = connection.getClientType() == HClient.SHOCKWAVE ? "shockwave" : "flash";
final ProcessBuilder pb = new ProcessBuilder(filePath, hotelType);
final Process p = pb.start(); final Process p = pb.start();
final HashSet<String> possibleData = new HashSet<>(); final HashSet<String> possibleData = new HashSet<>();

View File

@ -7,7 +7,7 @@ import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.*; import java.util.*;
public class LinuxHabboClient extends HabboClient { public class LinuxHabboClient implements HabboClient {
private static final String[] potentialProcessNames = {"--ppapi-flash-args", "plugin-container", "Habbo"}; private static final String[] potentialProcessNames = {"--ppapi-flash-args", "plugin-container", "Habbo"};
@ -25,8 +25,6 @@ public class LinuxHabboClient extends HabboClient {
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
public LinuxHabboClient(HConnection connection) { public LinuxHabboClient(HConnection connection) {
super(connection);
File folder = new File("/proc"); File folder = new File("/proc");
boolean found = false; boolean found = false;
@ -56,12 +54,6 @@ public class LinuxHabboClient extends HabboClient {
if (DEBUG) System.out.println("* Found flashclient " + potentialProcesses.size() + " potential processes"); if (DEBUG) System.out.println("* Found flashclient " + potentialProcesses.size() + " potential processes");
} }
@Override
public List<byte[]> getRC4cached() {
return new ArrayList<>();
}
private void refreshMemoryMaps() { private void refreshMemoryMaps() {
String filename = "/proc/"+this.PID+"/maps"; String filename = "/proc/"+this.PID+"/maps";
BufferedReader reader; BufferedReader reader;
@ -148,7 +140,7 @@ public class LinuxHabboClient extends HabboClient {
} }
public List<byte[]> getRC4possibilities() { public List<byte[]> getRC4Tables() {
int offset = 4; int offset = 4;
List<byte[]> resultSet = new ArrayList<>(); List<byte[]> resultSet = new ArrayList<>();

View File

@ -1,136 +0,0 @@
package gearth.protocol.memory.habboclient.macOs;
import gearth.encoding.HexEncoding;
import gearth.misc.Cacher;
import gearth.protocol.HConnection;
import gearth.protocol.HMessage;
import gearth.protocol.memory.habboclient.HabboClient;
import org.bouncycastle.util.encoders.Hex;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
public class MacOsHabboClient extends HabboClient {
public MacOsHabboClient(HConnection connection) {
super(connection);
connection.addTrafficListener(0, message -> {
if (message.getDestination() == HMessage.Direction.TOSERVER && message.getPacket().headerId() == PRODUCTION_ID) {
production = message.getPacket().readString();
}
});
}
private static final String OFFSETS_CACHE_KEY = "RC4Offsets";
private static final int PRODUCTION_ID = 4000;
private String production = "";
@Override
public List<byte[]> getRC4cached() {
List<byte[]> result = new ArrayList<>();
try {
List<String> possibleResults = readPossibleBytes(true);
if (possibleResults == null)
return new ArrayList<>();
for (String s : possibleResults)
result.add(HexEncoding.toBytes(s));
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
return result;
}
private ArrayList<String> readPossibleBytes(boolean useCache) throws IOException, URISyntaxException {
ProcessBuilder pb;
JSONObject revisionList = (JSONObject) Cacher.get(OFFSETS_CACHE_KEY);
if (revisionList == null) {
Cacher.put(OFFSETS_CACHE_KEY, new JSONObject());
revisionList = (JSONObject) Cacher.get(OFFSETS_CACHE_KEY);
}
assert revisionList != null;
JSONArray cachedOffsets;
if (revisionList.has(production))
cachedOffsets = (JSONArray) revisionList.get(production);
else
cachedOffsets = null;
StringJoiner joiner = new StringJoiner(" ");
if (useCache) {
if (cachedOffsets == null) {
return null;
}
for (Object s : cachedOffsets) {
joiner.add((String)s);
}
}
String g_mem = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent() + "/G-Mem";
if (!useCache)
pb = new ProcessBuilder(g_mem, hConnection.getClientHost() , Integer.toString(hConnection.getClientPort()));
else
pb = new ProcessBuilder(g_mem, hConnection.getClientHost() , Integer.toString(hConnection.getClientPort()), "-c" + joiner.toString());
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
ArrayList<String> possibleData = new ArrayList<>();
if (cachedOffsets == null) {
cachedOffsets = new JSONArray();
}
int count = 0;
while((line = reader.readLine()) != null) {
if (line.length() > 1) {
if (!useCache && (count++ % 2 == 0)) {
if (!cachedOffsets.toList().contains(line)) {
cachedOffsets.put(line);
System.out.println("[+] " + line);
}
}
else
possibleData.add(line);
}
}
revisionList.put(production, cachedOffsets);
Cacher.put(OFFSETS_CACHE_KEY, revisionList);
p.destroy();
return possibleData;
}
@Override
public List<byte[]> getRC4possibilities() {
List<byte[]> result = new ArrayList<>();
try {
ArrayList<String> possibleData = readPossibleBytes(false);
for (String possibleHexStr : possibleData) {
result.add(HexEncoding.toBytes(possibleHexStr));
}
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
return result;
}
}

View File

@ -1,56 +0,0 @@
package gearth.protocol.memory.habboclient.rust;
import gearth.encoding.HexEncoding;
import gearth.protocol.HConnection;
import gearth.protocol.memory.habboclient.HabboClient;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
public class RustHabboClient extends HabboClient {
public RustHabboClient(HConnection connection) {
super(connection);
}
@Override
public List<byte[]> getRC4cached() {
return new ArrayList<>();
}
public List<byte[]> getRC4possibilities() {
ArrayList<String> possibleData = new ArrayList<>();
try {
String g_mem = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent() + "/G-Mem";
ProcessBuilder pb = new ProcessBuilder(g_mem, hConnection.getClientHost() , Integer.toString(hConnection.getClientPort()));
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while((line = reader.readLine()) != null) {
if (line.length() > 1) {
System.out.println("[+] " + line);
possibleData.add(line);
}
}
} catch (URISyntaxException | IOException e) {
e.printStackTrace();
}
List<byte[]> ret = new ArrayList<>();
for (String possibleHexStr : possibleData)
ret.add(HexEncoding.toBytes(possibleHexStr));
return ret;
}
}

View File

@ -1,137 +0,0 @@
package gearth.protocol.memory.habboclient.windows;
import gearth.encoding.HexEncoding;
import gearth.misc.Cacher;
import gearth.protocol.HConnection;
import gearth.protocol.HMessage;
import gearth.protocol.memory.habboclient.HabboClient;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.*;
/**
* Created by Jonas on 27/06/2018.
*/
public class WindowsHabboClient extends HabboClient {
public WindowsHabboClient(HConnection connection) {
super(connection);
connection.addTrafficListener(0, message -> {
if (message.getDestination() == HMessage.Direction.TOSERVER && message.getPacket().headerId() == PRODUCTION_ID) {
production = message.getPacket().readString();
}
});
}
private static final String OFFSETS_CACHE_KEY = "RC4Offsets";
private static final int PRODUCTION_ID = 4000;
private String production = "";
@Override
public List<byte[]> getRC4cached() {
List<byte[]> result = new ArrayList<>();
try {
List<String> possibleResults = readPossibleBytes(true);
if (possibleResults == null)
return new ArrayList<>();
for (String s : possibleResults)
result.add(HexEncoding.toBytes(s));
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
return result;
}
private ArrayList<String> readPossibleBytes(boolean useCache) throws IOException, URISyntaxException {
ProcessBuilder pb;
JSONObject revisionList = (JSONObject) Cacher.get(OFFSETS_CACHE_KEY);
if (revisionList == null) {
Cacher.put(OFFSETS_CACHE_KEY, new JSONObject());
revisionList = (JSONObject) Cacher.get(OFFSETS_CACHE_KEY);
}
assert revisionList != null;
JSONArray cachedOffsets;
if (revisionList.has(production))
cachedOffsets = (JSONArray) revisionList.get(production);
else
cachedOffsets = null;
StringJoiner joiner = new StringJoiner(" ");
if (useCache) {
if (cachedOffsets == null) {
return null;
}
for (Object s : cachedOffsets) {
joiner.add((String)s);
}
}
String g_winmem = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent() + "\\G-Mem.exe";
String clientHost = hConnection.isRawIpMode() ? "null" : hConnection.getClientHost();
String clientPort = hConnection.isRawIpMode() ? "null" : hConnection.getClientPort() + "";
if (!useCache)
pb = new ProcessBuilder(g_winmem, clientHost , clientPort);
else
pb = new ProcessBuilder(g_winmem, clientHost , clientPort, "-c" + joiner.toString());
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
ArrayList<String> possibleData = new ArrayList<>();
if (cachedOffsets == null) {
cachedOffsets = new JSONArray();
}
int count = 0;
while((line = reader.readLine()) != null) {
if (line.length() > 1) {
if (!useCache && (count++ % 2 == 0)) {
if (!cachedOffsets.toList().contains(line)) {
cachedOffsets.put(line);
}
}
else
possibleData.add(line);
}
}
revisionList.put(production, cachedOffsets);
Cacher.put(OFFSETS_CACHE_KEY, revisionList);
p.destroy();
return possibleData;
}
@Override
public List<byte[]> getRC4possibilities() {
List<byte[]> result = new ArrayList<>();
try {
ArrayList<String> possibleData = readPossibleBytes(false);
for (String possibleHexStr : possibleData) {
result.add(HexEncoding.toBytes(possibleHexStr));
}
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
return result;
}
}

View File

@ -4,7 +4,7 @@ import gearth.protocol.crypto.RC4Base64;
import gearth.protocol.crypto.RC4Cipher; import gearth.protocol.crypto.RC4Cipher;
import gearth.protocol.memory.Rc4Obtainer; import gearth.protocol.memory.Rc4Obtainer;
import gearth.protocol.memory.habboclient.HabboClientFactory; import gearth.protocol.memory.habboclient.HabboClientFactory;
import gearth.protocol.memory.habboclient.shockwave.ShockwaveMemoryClient; 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 org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
@ -39,14 +39,9 @@ public class TestRc4Shockwave {
private final Semaphore waitSemaphore = new Semaphore(0); private final Semaphore waitSemaphore = new Semaphore(0);
private final AtomicReference<RC4Cipher> cipher = new AtomicReference<>(); private final AtomicReference<RC4Cipher> cipher = new AtomicReference<>();
private final ShockwaveMemoryClient mockShockwaveMemoryClient = new ShockwaveMemoryClient(null) { private final MemoryClient mockShockwaveMemoryClient = new MemoryClient(null) {
@Override @Override
public List<byte[]> getRC4cached() { public List<byte[]> getRC4Tables() {
return new ArrayList<>();
}
@Override
public List<byte[]> getRC4possibilities() {
return Arrays.asList(potentialTables); return Arrays.asList(potentialTables);
} }
}; };