diff --git a/G-Earth/pom.xml b/G-Earth/pom.xml index 0c88b75..cd15f4c 100644 --- a/G-Earth/pom.xml +++ b/G-Earth/pom.xml @@ -10,7 +10,7 @@ 1.8 - 9.4.51.v20230217 + 9.4.53.v20231009 1.3.12 @@ -209,7 +209,7 @@ org.json json - 20230227 + 20231013 org.fxmisc.richtext diff --git a/G-Earth/src/main/java/gearth/extensions/parsers/catalog/HOffer.java b/G-Earth/src/main/java/gearth/extensions/parsers/catalog/HOffer.java index 6a9cb58..c5073de 100644 --- a/G-Earth/src/main/java/gearth/extensions/parsers/catalog/HOffer.java +++ b/G-Earth/src/main/java/gearth/extensions/parsers/catalog/HOffer.java @@ -13,6 +13,7 @@ public class HOffer { private int priceInCredits; private int priceInActivityPoints; private int activityPointType; + private int priceInSilver; private boolean giftable; private List products = new ArrayList<>(); private int clubLevel; @@ -28,6 +29,7 @@ public class HOffer { this.priceInCredits = packet.readInteger(); this.priceInActivityPoints = packet.readInteger(); this.activityPointType = packet.readInteger(); + this.priceInSilver = packet.readInteger(); this.giftable = packet.readBoolean(); int productCount = packet.readInteger(); @@ -65,6 +67,10 @@ public class HOffer { return activityPointType; } + public int getPriceInSilver() { + return priceInSilver; + } + public boolean isGiftable() { return giftable; } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/ProxyProviderFactory.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/ProxyProviderFactory.java index 9070132..acd04a7 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/ProxyProviderFactory.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/ProxyProviderFactory.java @@ -38,7 +38,7 @@ public class ProxyProviderFactory { autoDetectHosts.add("game-it.habbo.com:30000"); autoDetectHosts.add("game-nl.habbo.com:30000"); autoDetectHosts.add("game-tr.habbo.com:30000"); - autoDetectHosts.add("game-us.habbo.com:38101"); + autoDetectHosts.add("game-us.habbo.com:30000"); autoDetectHosts.add("game-s2.habbo.com:30000"); List additionalCachedHotels = Cacher.getList(HOTELS_CACHE_KEY); diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java index b86a4fc..94d6869 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java @@ -6,6 +6,8 @@ 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.NitroAuthority; +import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager; import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxy; import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxyServerCallback; import gearth.protocol.connection.proxy.nitro.websocket.NitroWebsocketProxy; @@ -13,7 +15,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.ServerSocket; import java.util.concurrent.atomic.AtomicBoolean; public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCallback, StateChangeListener { @@ -32,11 +33,14 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa private String originalCookies; public NitroProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection) { + final NitroAuthority authority = new NitroAuthority(); + final NitroCertificateSniffingManager certificateManager = new NitroCertificateSniffingManager(authority); + this.proxySetter = proxySetter; this.stateSetter = stateSetter; this.connection = connection; - this.nitroHttpProxy = new NitroHttpProxy(this); - this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this); + this.nitroHttpProxy = new NitroHttpProxy(this, certificateManager); + this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this, certificateManager); this.abortLock = new AtomicBoolean(); } @@ -122,7 +126,7 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa public String replaceWebsocketServer(String configUrl, String websocketUrl) { originalWebsocketUrl = websocketUrl; - return String.format("ws://127.0.0.1:%d", websocketPort); + return String.format("wss://127.0.0.1:%d", websocketPort); } @Override diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java index 35f4c2d..c5c3d63 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java @@ -20,12 +20,31 @@ public class NitroCertificateSniffingManager implements MitmManager { private static final boolean DEBUG = false; private final BouncyCastleSslEngineSource sslEngineSource; + private final Authority authority; - public NitroCertificateSniffingManager(Authority authority) throws RootCertificateException { + public NitroCertificateSniffingManager(Authority authority) { + this.authority = authority; try { sslEngineSource = new BouncyCastleSslEngineSource(authority, true, true, null); } catch (final Exception e) { - throw new RootCertificateException("Errors during assembling root CA.", e); + throw new RuntimeException(new RootCertificateException("Errors during assembling root CA.", e)); + } + } + + public Authority getAuthority() { + return authority; + } + + public SSLEngine websocketSslEngine(String commonName) { + final SubjectAlternativeNameHolder san = new SubjectAlternativeNameHolder(); + + san.addDomainName("localhost"); + san.addIpAddress("127.0.0.1"); + + try { + return sslEngineSource.createCertForHost(commonName, san); + } catch (Exception e) { + throw new FakeCertificateException("Failed to create WebSocket certificate", e); } } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java index c0098f3..cb7e0ba 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java @@ -12,8 +12,6 @@ import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import org.littleshoot.proxy.HttpProxyServer; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; -import org.littleshoot.proxy.mitm.Authority; -import org.littleshoot.proxy.mitm.RootCertificateException; import java.io.File; import java.io.IOException; @@ -25,20 +23,20 @@ public class NitroHttpProxy { private static final String ADMIN_WARNING_KEY = "admin_warning_dialog"; private static final AtomicBoolean SHUTDOWN_HOOK = new AtomicBoolean(); - private final Authority authority; private final NitroOsFunctions osFunctions; private final NitroHttpProxyServerCallback serverCallback; + private final NitroCertificateSniffingManager certificateManager; private HttpProxyServer proxyServer = null; - public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback) { + public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback, NitroCertificateSniffingManager certificateManager) { this.serverCallback = serverCallback; - this.authority = new NitroAuthority(); + this.certificateManager = certificateManager; this.osFunctions = NitroOsFunctionsFactory.create(); } private boolean initializeCertificate() { - final File certificate = this.authority.aliasFile(".pem"); + final File certificate = this.certificateManager.getAuthority().aliasFile(".pem"); // All good if certificate is already trusted. if (this.osFunctions.isRootCertificateTrusted(certificate)) { @@ -80,7 +78,7 @@ public class NitroHttpProxy { return false; } - return this.osFunctions.installRootCertificate(this.authority.aliasFile(".pem")); + return this.osFunctions.installRootCertificate(this.certificateManager.getAuthority().aliasFile(".pem")); } /** @@ -100,33 +98,28 @@ public class NitroHttpProxy { public boolean start() { setupShutdownHook(); - try { - proxyServer = DefaultHttpProxyServer.bootstrap() - .withPort(NitroConstants.HTTP_PORT) - .withManInTheMiddle(new NitroCertificateSniffingManager(authority)) - .withFiltersSource(new NitroHttpProxyFilterSource(serverCallback)) - .withTransparent(true) - .start(); + proxyServer = DefaultHttpProxyServer.bootstrap() + .withPort(NitroConstants.HTTP_PORT) + .withManInTheMiddle(this.certificateManager) + .withFiltersSource(new NitroHttpProxyFilterSource(serverCallback)) + .withTransparent(true) + .start(); - if (!initializeCertificate()) { - proxyServer.stop(); + if (!initializeCertificate()) { + proxyServer.stop(); - System.out.println("Failed to initialize certificate"); - return false; - } - - if (!registerProxy()) { - proxyServer.stop(); - - System.out.println("Failed to register certificate"); - return false; - } - - return true; - } catch (RootCertificateException e) { - e.printStackTrace(); + System.out.println("Failed to initialize certificate"); return false; } + + if (!registerProxy()) { + proxyServer.stop(); + + System.out.println("Failed to register certificate"); + return false; + } + + return true; } public void pause() { diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java index b9795ec..5d9978d 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java @@ -16,19 +16,24 @@ import java.util.regex.Pattern; public class NitroHttpProxyFilter extends HttpFiltersAdapter { - private static final String NitroConfigSearch = "\"socket.url\""; + private static final String NitroConfigSearch = "socket.url"; private static final String NitroClientSearch = "configurationUrls:"; - private static final Pattern NitroConfigPattern = Pattern.compile("\"socket\\.url\":.?\"(wss?://.*?)\"", Pattern.MULTILINE); + private static final Pattern NitroConfigPattern = Pattern.compile("[\"']socket\\.url[\"']:(\\s+)?[\"'](wss?:.*?)[\"']", Pattern.MULTILINE); // https://developers.cloudflare.com/fundamentals/get-started/reference/cloudflare-cookies/ private static final HashSet CloudflareCookies = new HashSet<>(Arrays.asList( "__cflb", "__cf_bm", + "__cfseq", "cf_ob_info", "cf_use_ob", "__cfwaitingroom", "__cfruid", - "cf_clearance" + "_cfuvid", + "cf_clearance", + "cf_chl_rc_i", + "cf_chl_rc_ni", + "cf_chl_rc_m" )); private static final String HeaderAcceptEncoding = "Accept-Encoding"; @@ -95,11 +100,11 @@ public class NitroHttpProxyFilter extends HttpFiltersAdapter { final Matcher matcher = NitroConfigPattern.matcher(responseBody); if (matcher.find()) { - final String originalWebsocket = matcher.group(1); + final String originalWebsocket = matcher.group(2).replace("\\/", "/"); final String replacementWebsocket = callback.replaceWebsocketServer(this.url, originalWebsocket); if (replacementWebsocket != null) { - responseBody = responseBody.replace(originalWebsocket, replacementWebsocket); + responseBody = responseBody.replace(matcher.group(2), replacementWebsocket); responseModified = true; } } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java new file mode 100644 index 0000000..9e12b69 --- /dev/null +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java @@ -0,0 +1,20 @@ +package gearth.protocol.connection.proxy.nitro.http; + +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import javax.net.ssl.SSLEngine; + +public class NitroSslContextFactory extends SslContextFactory.Server { + + private final NitroCertificateSniffingManager certificateManager; + + public NitroSslContextFactory(NitroCertificateSniffingManager certificateManager) { + this.certificateManager = certificateManager; + } + + @Override + public SSLEngine newSSLEngine(String host, int port) { + System.out.printf("[NitroSslContextFactory] Creating SSLEngine for %s:%d%n", host, port); + return certificateManager.websocketSslEngine(host); + } +} diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/NitroOsFunctionsFactory.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/NitroOsFunctionsFactory.java index 18bbf7c..3c46e20 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/NitroOsFunctionsFactory.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/NitroOsFunctionsFactory.java @@ -1,6 +1,7 @@ package gearth.protocol.connection.proxy.nitro.os; import gearth.misc.OSValidator; +import gearth.protocol.connection.proxy.nitro.os.macos.NitroMacOS; import gearth.protocol.connection.proxy.nitro.os.windows.NitroWindows; import org.apache.commons.lang3.NotImplementedException; @@ -15,7 +16,10 @@ public final class NitroOsFunctionsFactory { throw new NotImplementedException("unix nitro is not implemented yet"); } - throw new NotImplementedException("macOS nitro is not implemented yet"); - } + if (OSValidator.isMac()) { + return new NitroMacOS(); + } + throw new NotImplementedException("unsupported operating system"); + } } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/macos/NitroMacOS.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/macos/NitroMacOS.java new file mode 100644 index 0000000..35d5ff1 --- /dev/null +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/macos/NitroMacOS.java @@ -0,0 +1,96 @@ +package gearth.protocol.connection.proxy.nitro.os.macos; + +import gearth.misc.RuntimeUtil; +import gearth.protocol.connection.proxy.nitro.os.NitroOsFunctions; + +import java.io.File; +import java.io.IOException; + +public class NitroMacOS implements NitroOsFunctions { + + /** + * Semicolon separated hosts to ignore for proxying. + */ + private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;"; + + /** + * Checks if the certificate is trusted by the local machine. + * @param certificate Absolute path to the certificate. + * @return true if trusted + */ + @Override + public boolean isRootCertificateTrusted(File certificate) { + try { + final String output = RuntimeUtil.getCommandOutput(new String[] {"sh", "-c", "security verify-cert -c \"" + certificate.getAbsolutePath() + "\""}); + + return !output.contains("CSSMERR_TP_NOT_TRUSTED"); + } catch (IOException e) { + e.printStackTrace(); + } + + return false; + } + + @Override + public boolean installRootCertificate(File certificate) { + final String certificatePath = certificate.getAbsolutePath(); + + try { + // Install the certificate + Process process = new ProcessBuilder("sh", "-c", "sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain \"" + certificatePath + "\"") + .inheritIO() + .start(); + + // Wait for the process to complete + int exitCode = process.waitFor(); + if (exitCode != 0) { + System.out.printf("security add-trusted-cert command exited with exit code %d%n", exitCode); + return false; + } + + return true; + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + return false; + } + + @Override + public boolean registerSystemProxy(String host, int port) { + try { + final String[] ignoreHosts = PROXY_IGNORE.split(";"); + + // Enable proxy + Runtime.getRuntime().exec("networksetup -setwebproxy Wi-Fi " + host + " " + port); + Runtime.getRuntime().exec("networksetup -setsecurewebproxy Wi-Fi " + host + " " + port); + + // Set proxy bypass domains + for (String ignoreHost : ignoreHosts) { + Runtime.getRuntime().exec("networksetup -setproxybypassdomains Wi-Fi " + ignoreHost); + } + + return true; + } catch (IOException e) { + e.printStackTrace(); + } + + return false; + } + + @Override + public boolean unregisterSystemProxy() { + try { + // Disable proxy + Runtime.getRuntime().exec("networksetup -setwebproxystate Wi-Fi off"); + Runtime.getRuntime().exec("networksetup -setsecurewebproxystate Wi-Fi off"); + + return true; + } catch (IOException e) { + e.printStackTrace(); + } + + return false; + } + +} \ No newline at end of file diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/windows/NitroWindows.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/windows/NitroWindows.java index ec55105..fb7d370 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/windows/NitroWindows.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/windows/NitroWindows.java @@ -15,7 +15,8 @@ public class NitroWindows implements NitroOsFunctions { /** * Semicolon separated hosts to ignore for proxying. */ - private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;"; + // habba.io; + private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;challenges.cloudflare.com;"; /** * Checks if the certificate is trusted by the local machine. diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java index c60f8c2..a21244c 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java @@ -4,7 +4,8 @@ import gearth.protocol.HConnection; import gearth.protocol.connection.HProxySetter; import gearth.protocol.connection.HStateSetter; import gearth.protocol.connection.proxy.nitro.NitroProxyProvider; -import org.eclipse.jetty.server.Connector; +import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager; +import gearth.protocol.connection.proxy.nitro.http.NitroSslContextFactory; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -21,19 +22,37 @@ public class NitroWebsocketProxy { private final HStateSetter stateSetter; private final HConnection connection; private final NitroProxyProvider proxyProvider; + private final NitroCertificateSniffingManager certificateManager; private final Server server; + private final int serverPort; - public NitroWebsocketProxy(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) { + public NitroWebsocketProxy(HProxySetter proxySetter, + HStateSetter stateSetter, + HConnection connection, + NitroProxyProvider proxyProvider, + NitroCertificateSniffingManager certificateManager) { this.proxySetter = proxySetter; this.stateSetter = stateSetter; this.connection = connection; this.proxyProvider = proxyProvider; - this.server = new Server(0); + this.certificateManager = certificateManager; + this.server = new Server(); + this.serverPort = 0; } public boolean start() { try { + // Configure SSL. + final NitroSslContextFactory sslContextFactory = new NitroSslContextFactory(this.certificateManager); + final ServerConnector sslConnector = new ServerConnector(server, sslContextFactory); + + sslConnector.setPort(this.serverPort); + + // Add SSL to the server. + server.addConnector(sslConnector); + + // Configure the WebSocket. final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); diff --git a/docs/README.md b/docs/README.md index 0ab39aa..7441a57 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,6 +39,7 @@ G-Python1 | Python | sirjonasxx | https://github.com/sirjonasxx/G-Pyt Xabbo | C# | b7 | https://github.com/b7c/Xabbo.GEarth Xabbo scripter2 | C# | b7 | https://github.com/b7c/Xabbo.Scripter G-Node | Node.js | WiredSpast | https://github.com/WiredSpast/G-Node +G-Rust | Rust | WiredSpast | https://github.com/WiredSpast/G-Rust GProgrammer3 | Javascript | at15four2020 | https://github.com/at15four2020/GProgrammer/wiki 1: built-in in G-Earth through the [live scripting console](https://github.com/sirjonasxx/G-Earth/wiki/G-Python-qtConsole)