Added websocket proxying for nitro with broken outgoing packets

This commit is contained in:
UnfamiliarLegacy 2021-11-26 06:19:07 +01:00
parent 1c6d086bff
commit 368a21ffcf
11 changed files with 395 additions and 26 deletions

View File

@ -0,0 +1,11 @@
package gearth.protocol.connection.proxy.nitro;
public final class NitroConstants {
public static final int PORT_WEBSOCKET = 2096;
public static final int PORT_HTTP = 9090;
public static final int WEBSOCKET_BUFFER_SIZE = 1024 * 1024 * 10;
public static final String WEBSOCKET_REVISION = "PRODUCTION-201611291003-338511768";
}

View File

@ -1,34 +1,52 @@
package gearth.protocol.connection.proxy.nitro;
import gearth.protocol.HConnection;
import gearth.protocol.StateChangeListener;
import gearth.protocol.connection.HProxySetter;
import gearth.protocol.connection.HState;
import gearth.protocol.connection.HStateSetter;
import gearth.protocol.connection.proxy.ProxyProvider;
import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxy;
import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxyServerCallback;
import gearth.protocol.connection.proxy.nitro.websocket.NitroWebsocketProxy;
import java.io.IOException;
public class NitroProxyProvider implements ProxyProvider {
public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCallback, StateChangeListener {
private final HProxySetter proxySetter;
private final HStateSetter stateSetter;
private final HConnection hConnection;
private final NitroHttpProxy nitroProxy;
private final HConnection connection;
private final NitroHttpProxy nitroHttpProxy;
private final NitroWebsocketProxy nitroWebsocketProxy;
public NitroProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection hConnection) {
private String originalWebsocketUrl;
public NitroProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection) {
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.hConnection = hConnection;
this.nitroProxy = new NitroHttpProxy();
this.connection = connection;
this.nitroHttpProxy = new NitroHttpProxy(this);
this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this);
}
public String getOriginalWebsocketUrl() {
return originalWebsocketUrl;
}
@Override
public void start() throws IOException {
if (!nitroProxy.start()) {
System.out.println("Failed to start nitro proxy");
connection.getStateObservable().addListener(this);
stateSetter.setState(HState.NOT_CONNECTED);
if (!nitroHttpProxy.start()) {
System.out.println("Failed to start nitro proxy");
abort();
return;
}
if (!nitroWebsocketProxy.start()) {
System.out.println("Failed to start nitro websocket proxy");
abort();
return;
}
@ -41,13 +59,35 @@ public class NitroProxyProvider implements ProxyProvider {
new Thread(() -> {
try {
nitroProxy.stop();
nitroHttpProxy.stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
stateSetter.setState(HState.NOT_CONNECTED);
}
try {
nitroWebsocketProxy.stop();
} catch (Exception e) {
e.printStackTrace();
}
stateSetter.setState(HState.NOT_CONNECTED);
connection.getStateObservable().removeListener(this);
}).start();
}
@Override
public String replaceWebsocketServer(String configUrl, String websocketUrl) {
originalWebsocketUrl = websocketUrl;
return String.format("ws://127.0.0.1:%d", NitroConstants.PORT_WEBSOCKET);
}
@Override
public void stateChanged(HState oldState, HState newState) {
if (oldState == HState.WAITING_FOR_CLIENT && newState == HState.CONNECTED) {
// Unregister but do not stop http proxy.
// We are not stopping the http proxy because some requests might still require it to be running.
nitroHttpProxy.pause();
}
}
}

View File

@ -1,5 +1,6 @@
package gearth.protocol.connection.proxy.nitro.http;
import gearth.protocol.connection.proxy.nitro.NitroConstants;
import gearth.protocol.connection.proxy.nitro.os.NitroOsFunctions;
import gearth.protocol.connection.proxy.nitro.os.NitroOsFunctionsFactory;
import org.littleshoot.proxy.HttpProxyServer;
@ -12,10 +13,12 @@ public class NitroHttpProxy {
private final Authority authority;
private final NitroOsFunctions osFunctions;
private final NitroHttpProxyServerCallback serverCallback;
private HttpProxyServer proxyServer = null;
public NitroHttpProxy() {
public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback) {
this.serverCallback = serverCallback;
this.authority = new NitroAuthority();
this.osFunctions = NitroOsFunctionsFactory.create();
}
@ -28,7 +31,7 @@ public class NitroHttpProxy {
* Register HTTP(s) proxy on the system.
*/
private boolean registerProxy() {
return this.osFunctions.registerSystemProxy("127.0.0.1", 9090);
return this.osFunctions.registerSystemProxy("127.0.0.1", NitroConstants.PORT_HTTP);
}
/**
@ -39,18 +42,11 @@ public class NitroHttpProxy {
}
public boolean start() {
try {
proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(9090)
.withPort(NitroConstants.PORT_HTTP)
.withManInTheMiddle(new CertificateSniffingMitmManager(authority))
// TODO: Replace lambda with some class
.withFiltersSource(new NitroHttpProxyFilterSource((configUrl, websocketUrl) -> {
System.out.printf("Found %s at %s%n", websocketUrl, configUrl);
return "wss://127.0.0.1:2096";
}))
.withFiltersSource(new NitroHttpProxyFilterSource(serverCallback))
.start();
if (!initializeCertificate()) {
@ -74,10 +70,14 @@ public class NitroHttpProxy {
}
}
public void stop() {
public void pause() {
if (!unregisterProxy()) {
System.out.println("Failed to unregister system proxy, please check manually");
}
}
public void stop() {
pause();
if (proxyServer == null) {
return;

View File

@ -0,0 +1,17 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
public class NitroForwarder {
private final HConnection connection;
private final NitroSession client;
private final NitroSession server;
public NitroForwarder(HConnection connection, NitroSession client, NitroSession server) {
this.connection = connection;
this.client = client;
this.server = server;
}
}

View File

@ -0,0 +1,56 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HMessage;
import gearth.protocol.HPacket;
import gearth.protocol.packethandler.PacketHandler;
import gearth.services.extension_handler.ExtensionHandler;
import gearth.services.extension_handler.OnHMessageHandled;
import java.io.IOException;
import java.nio.ByteBuffer;
public class NitroPacketHandler extends PacketHandler {
private final HMessage.Direction direction;
private final NitroSession session;
protected NitroPacketHandler(HMessage.Direction direction, NitroSession session, ExtensionHandler extensionHandler, Object[] trafficObservables) {
super(extensionHandler, trafficObservables);
this.direction = direction;
this.session = session;
}
@Override
public boolean sendToStream(byte[] buffer) {
session.getSession().getAsyncRemote().sendBinary(ByteBuffer.wrap(buffer));
return true;
}
@Override
public void act(byte[] buffer) throws IOException {
HMessage hMessage = new HMessage(new HPacket(deepCopy(buffer)), direction, currentIndex);
OnHMessageHandled afterExtensionIntercept = hMessage1 -> {
notifyListeners(2, hMessage1);
if (!hMessage1.isBlocked()) {
sendToStream(hMessage1.getPacket().toBytes());
}
};
notifyListeners(0, hMessage);
notifyListeners(1, hMessage);
extensionHandler.handle(hMessage, afterExtensionIntercept);
currentIndex++;
}
public static byte[] deepCopy(byte[] org) {
if (org == null)
return null;
byte[] result = new byte[org.length];
System.arraycopy(org, 0, result, 0, org.length);
return result;
}
}

View File

@ -0,0 +1,9 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import javax.websocket.Session;
public interface NitroSession {
Session getSession();
}

View File

@ -0,0 +1,78 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
import gearth.protocol.HMessage;
import gearth.protocol.connection.*;
import gearth.protocol.connection.proxy.nitro.NitroConstants;
import gearth.protocol.connection.proxy.nitro.NitroProxyProvider;
import gearth.services.internal_extensions.uilogger.hexdumper.Hexdump;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint(value = "/")
public class NitroWebsocketClient implements NitroSession {
private final HProxySetter proxySetter;
private final HStateSetter stateSetter;
private final HConnection connection;
private final NitroProxyProvider proxyProvider;
private final NitroWebsocketServer server;
private final NitroPacketHandler packetHandler;
private Session activeSession = null;
private HProxy proxy = null;
public NitroWebsocketClient(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) {
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.proxyProvider = proxyProvider;
this.server = new NitroWebsocketServer(connection, this);
this.packetHandler = new NitroPacketHandler(HMessage.Direction.TOSERVER, server, connection.getExtensionHandler(), connection.getTrafficObservables());
}
@OnOpen
public void onOpen(Session session) throws IOException {
activeSession = session;
activeSession.setMaxBinaryMessageBufferSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
server.connect(proxyProvider.getOriginalWebsocketUrl());
proxy = new HProxy(HClient.NITRO, "", "", -1, -1, "");
proxy.verifyProxy(
this.server.getPacketHandler(),
this.packetHandler,
NitroConstants.WEBSOCKET_REVISION,
"HTML5" // TODO: What is its purpose?
);
proxySetter.setProxy(proxy);
stateSetter.setState(HState.CONNECTED);
}
@OnMessage
public void onMessage(byte[] b, Session session) throws IOException {
System.out.printf("onMessage (%d)%n", b.length);
System.out.println(session);
System.out.println(Hexdump.hexdump(b));
packetHandler.act(b);
}
@OnClose
public void onClose(Session session) throws IOException {
activeSession = null;
}
@OnError
public void onError(Session session, Throwable throwable) {
}
@Override
public Session getSession() {
return activeSession;
}
}

View File

@ -0,0 +1,66 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
import gearth.protocol.connection.HProxySetter;
import gearth.protocol.connection.HStateSetter;
import gearth.protocol.connection.proxy.nitro.NitroConstants;
import gearth.protocol.connection.proxy.nitro.NitroProxyProvider;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
public class NitroWebsocketProxy {
private final HProxySetter proxySetter;
private final HStateSetter stateSetter;
private final HConnection connection;
private final NitroProxyProvider proxyProvider;
private final Server server;
public NitroWebsocketProxy(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) {
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.proxyProvider = proxyProvider;
this.server = new Server(NitroConstants.PORT_WEBSOCKET);
}
public boolean start() {
try {
final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
final HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { context });
final ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
wscontainer.addEndpoint(ServerEndpointConfig.Builder
.create(NitroWebsocketClient.class, "/")
.configurator(new NitroWebsocketServerConfigurator(proxySetter, stateSetter, connection, proxyProvider))
.build());
server.setHandler(handlers);
server.start();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public void stop() {
try {
server.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,65 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
import gearth.protocol.HMessage;
import gearth.protocol.connection.proxy.nitro.NitroConstants;
import gearth.protocol.packethandler.PacketHandler;
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
@ClientEndpoint
public class NitroWebsocketServer implements NitroSession {
private final PacketHandler packetHandler;
private Session activeSession = null;
public NitroWebsocketServer(HConnection connection, NitroWebsocketClient client) {
this.packetHandler = new NitroPacketHandler(HMessage.Direction.TOCLIENT, client, connection.getExtensionHandler(), connection.getTrafficObservables());
}
public void connect(String websocketUrl) throws IOException {
try {
ContainerProvider.getWebSocketContainer().connectToServer(this, URI.create(websocketUrl));
} catch (DeploymentException e) {
throw new IOException("Failed to deploy websocket client", e);
}
}
@OnOpen
public void onOpen(Session Session) {
this.activeSession = Session;
this.activeSession.setMaxBinaryMessageBufferSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
}
@OnMessage
public void onMessage(byte[] b, Session session) throws IOException {
//System.out.printf("onMessage (%d)%n", b.length);
//System.out.println(session);
//System.out.println(Hexdump.hexdump(b));
packetHandler.act(b);
}
@OnClose
public void onClose(Session userSession, CloseReason reason) {
//System.out.println("closing websocket");
}
@OnError
public void onError(Session session, Throwable throwable) {
//System.out.println("onError");
//System.out.println(session);
//System.out.println(throwable);
}
@Override
public Session getSession() {
return activeSession;
}
public PacketHandler getPacketHandler() {
return packetHandler;
}
}

View File

@ -0,0 +1,28 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
import gearth.protocol.connection.HProxySetter;
import gearth.protocol.connection.HStateSetter;
import gearth.protocol.connection.proxy.nitro.NitroProxyProvider;
import javax.websocket.server.ServerEndpointConfig;
public class NitroWebsocketServerConfigurator extends ServerEndpointConfig.Configurator {
private final HProxySetter proxySetter;
private final HStateSetter stateSetter;
private final HConnection connection;
private final NitroProxyProvider proxyProvider;
public NitroWebsocketServerConfigurator(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) {
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.proxyProvider = proxyProvider;
}
@Override
public <T> T getEndpointInstance(Class<T> endpointClass) {
return (T) new NitroWebsocketClient(proxySetter, stateSetter, connection, proxyProvider);
}
}

View File

@ -114,8 +114,7 @@ public class PacketInfoManager {
if (clientType == HClient.UNITY) {
result.addAll(new GEarthUnityPacketInfoProvider(hotelversion).provide());
}
else if (clientType == HClient.FLASH) {
} else if (clientType == HClient.FLASH || clientType == HClient.NITRO) {
try {
List<RemotePacketInfoProvider> providers = new ArrayList<>();
providers.add(new HarblePacketInfoProvider(hotelversion));