From abc3706701443806d52b0cabaa14ea062205ba3c Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Mon, 15 Jan 2024 22:12:44 -0800 Subject: [PATCH] Working IPC --- .../nosession/NoSessionLoadingPlugin.java | 24 ++++-- .../mixin/client/YggdrasilSessionMixin.java | 14 +-- .../nosession/relaunch/LegacyRelaunch.java | 1 + .../tweaks/initial/InitialTweaker.java | 4 +- .../malwarefight/nosession/utils/Utils.java | 86 ++++++++++++------- .../nosession/win/HandleSocket.java | 42 +++++++++ .../malwarefight/nosession/win/Kernel32.java | 78 +++++++++++++++++ .../nosession/win/WindowsSandbox.java | 6 +- .../gq/malwarefight/tokenapp/IPCProvider.java | 9 ++ .../java/gq/malwarefight/tokenapp/Main.java | 41 ++++----- .../tokenapp/NetworkIPCProvider.java | 45 ++++++++++ .../malwarefight/tokenapp/SimpleSocket.java | 11 +++ .../malwarefight/tokenapp/SocketThread.java | 4 +- .../tokenapp/WindowsPipeIPCProvider.java | 66 ++++++++++++++ .../tokenapp/WrappedSimpleSocket.java | 29 +++++++ 15 files changed, 385 insertions(+), 75 deletions(-) create mode 100644 src/main/java/gq/malwarefight/nosession/win/HandleSocket.java create mode 100644 src/main/java/gq/malwarefight/nosession/win/Kernel32.java create mode 100644 src/main/java/gq/malwarefight/tokenapp/IPCProvider.java create mode 100644 src/main/java/gq/malwarefight/tokenapp/NetworkIPCProvider.java create mode 100644 src/main/java/gq/malwarefight/tokenapp/SimpleSocket.java create mode 100644 src/main/java/gq/malwarefight/tokenapp/WindowsPipeIPCProvider.java create mode 100644 src/main/java/gq/malwarefight/tokenapp/WrappedSimpleSocket.java diff --git a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java index 7e3c4ed..f7d8487 100644 --- a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java +++ b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java @@ -85,13 +85,19 @@ public static File copyNativesDir() throws IOException { return tempdir; } + private static boolean hasNatives() { + return SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_WINDOWS && WindowsSandbox.nativesAvaliable; + } + public static void relaunchProcess(Matcher m) throws Exception { RuntimeMXBean rmb = ManagementFactory.getRuntimeMXBean(); - if (!(SystemUtils.IS_OS_LINUX | SystemUtils.IS_OS_WINDOWS) || !System.getProperty("os.arch").equals("amd64")) { + if (!hasNatives()) { + Utils.setToken(m.group("token"), "network"); ArrayList args = new ArrayList<>(); args.add(Utils.getJavaExe(true)); args.add("-cp"); args.add(System.getProperty("java.class.path")); + args.add("-Dgq.malwarefight.nosession.ipc=network"); args.addAll(rmb.getInputArguments()); String newArgs = m.replaceAll("--accessToken "); args.addAll(Arrays.asList(newArgs.split(" "))); @@ -104,6 +110,7 @@ public static void relaunchProcess(Matcher m) throws Exception { throw new RuntimeException(e); } } else if (SystemUtils.IS_OS_LINUX) { + Utils.setToken(m.group("token"), "network"); String sandboxXdgDir = "/run/user/" + Libc.geteuid(); BubblewrapBuilder builder = new BubblewrapBuilder() .setCommand(Utils.getJavaExe(true)) @@ -180,6 +187,7 @@ public static void relaunchProcess(Matcher m) throws Exception { } builder.addArgs(rmb.getInputArguments().toArray(new String[0])); builder.addArgs("-cp", classPath + System.getProperty("path.separator") + Utils.getLibraryPathAsFile(NoSessionLoadingPlugin.class).getAbsolutePath()); + builder.addArgs("-Dgq.malwarefight.nosession.ipc=network"); builder.addArgs("net.minecraft.launchwrapper.Launch"); // blackboard erases the type, but we are sure that launchArgs will always be a HashMap //noinspection unchecked @@ -187,6 +195,7 @@ public static void relaunchProcess(Matcher m) throws Exception { builder.addArgs("--tweakClass", "net.minecraftforge.fml.common.launcher.FMLTweaker"); builder.build().start(); } else { + Utils.setToken(m.group("token"), "windowspipe"); ArrayList roMounts = new ArrayList<>(); String classPath = rmb.getClassPath(); for (String path : classPath.split(System.getProperty("path.separator"))) { @@ -195,11 +204,11 @@ public static void relaunchProcess(Matcher m) throws Exception { } roMounts.add(Utils.getLibraryPathAsFile(NoSessionLoadingPlugin.class).getAbsolutePath()); roMounts.add(System.getProperty("java.home")); - System.out.println(roMounts); ArrayList args = new ArrayList<>(); args.add(Utils.getJavaExe(true)); args.addAll(Arrays.asList(rmb.getInputArguments().toArray(new String[0]))); args.addAll(Arrays.asList("-cp", classPath + System.getProperty("path.separator") + Utils.getLibraryPathAsFile(NoSessionLoadingPlugin.class).getAbsolutePath())); + args.add("-Dgq.malwarefight.nosession.ipc=windowspipe"); args.add("net.minecraft.launchwrapper.Launch"); // blackboard erases the type, but we are sure that launchArgs will always be a HashMap //noinspection unchecked @@ -220,10 +229,10 @@ public static void relaunchProcess(Matcher m) throws Exception { Launch.minecraftHome.getAbsolutePath(), Launch.assetsDir.getAbsolutePath(), copiedNativesDir, + "\\\\.\\pipe\\" }, roMounts.toArray(new String[0]), args.toArray(new String[0]) -// new String[]{"C:\\Windows\\System32\\cmd.exe"} )) { throw new RuntimeException("Failed to start sandbox!"); } @@ -232,12 +241,12 @@ public static void relaunchProcess(Matcher m) throws Exception { } static { + System.out.println(System.getProperty("java.io.tmpdir")); addSelfToClassLoader(); try { Pattern mcJWT = Pattern.compile("--accessToken +(?eyJhbGciOiJIUzI1NiJ9\\.[A-Za-z0-9-_]*\\.[A-Za-z0-9-_]*)"); Matcher m = mcJWT.matcher(System.getProperty("sun.java.command")); if (m.find()) { - Utils.setToken(m.group("token")); relaunchProcess(m); } else { // test if we can find the token in Launch.blackboard.get("launchArgs") @@ -246,22 +255,21 @@ public static void relaunchProcess(Matcher m) throws Exception { Pattern pattern = Pattern.compile("(?ey[A-Za-z0-9-_]*\\.[A-Za-z0-9-_]*\\.[A-Za-z0-9-_]*)"); Matcher match = pattern.matcher(launchArgs.get("--accessToken")); if (match.find()) { - Utils.setToken(match.group("token")); // relaunch now ! System.out.println("======================="); System.out.println("NoSession: relaunching without the token"); System.out.println("======================="); if (SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_WINDOWS) { - if (new File("/.flatpak-info").exists()) { + if (SystemUtils.IS_OS_LINUX && new File("/.flatpak-info").exists()) { LogManager.getLogger().warn("Bubblewrap sandboxing doesn't work on Flatpak. Malicious mods " + "could still steal long-lived session secrets"); - } else if ("prismlauncher-bwrap".equals(System.getenv("MINECRAFT_SANDBOX_VARIANT"))) { + } else if (SystemUtils.IS_OS_LINUX && "prismlauncher-bwrap".equals(System.getenv("MINECRAFT_SANDBOX_VARIANT"))) { LogManager.getLogger().info("Detected PrismLauncher sandbox, continuing..."); } else if (System.getProperties().containsKey("gq.malwarefight.nosession.disable_sandbox")) { LogManager.getLogger().info("Skipping sandboxing because gq.malwarefight.nosession.disable_sandbox was set..."); LogManager.getLogger().info("This may reduce security, make sure you know what you're doing!"); } else { - relaunchProcess(m); + relaunchProcess(match); } } try { diff --git a/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java b/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java index 3405f3c..539310e 100644 --- a/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java +++ b/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java @@ -4,13 +4,12 @@ import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; import gq.malwarefight.nosession.utils.Utils; +import gq.malwarefight.tokenapp.SimpleSocket; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.io.IOException; -import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -21,7 +20,7 @@ public class YggdrasilSessionMixin { public void joinServer(GameProfile profile, String authenticationToken, String serverId, CallbackInfo ci) throws AuthenticationException { try { if (authenticationToken.equals("")) { // this token has been messed with by the Tweaker - Socket socket = Utils.getProperSocket(profile.getId()); + SimpleSocket socket = Utils.getProperSocket(profile.getId()); try { if (socket == null) { throw new NullPointerException("Socket is null!"); @@ -39,7 +38,8 @@ public void joinServer(GameProfile profile, String authenticationToken, String s } else { throw new AuthenticationException("NoSession: Upstream error or incompatible version"); } - } catch (Exception e) { + } catch (Throwable e) { + e.printStackTrace(); throw new AuthenticationException("NoSession (block 1): " + e.getClass().getName() + ": " + e.getMessage()); } @@ -52,11 +52,11 @@ public void joinServer(GameProfile profile, String authenticationToken, String s // impossible to close/unusable } } - } catch (Exception e) { + } catch (Throwable e) { + e.printStackTrace(); if (e instanceof AuthenticationException) throw e; - e.printStackTrace(); - throw new AuthenticationException("NoSession (block 2): " + e.getMessage()); + throw new AuthenticationException("NoSession (block 2): " + e.getMessage(), e); } } } \ No newline at end of file diff --git a/src/main/java/gq/malwarefight/nosession/relaunch/LegacyRelaunch.java b/src/main/java/gq/malwarefight/nosession/relaunch/LegacyRelaunch.java index 1137ed5..aa65207 100644 --- a/src/main/java/gq/malwarefight/nosession/relaunch/LegacyRelaunch.java +++ b/src/main/java/gq/malwarefight/nosession/relaunch/LegacyRelaunch.java @@ -34,6 +34,7 @@ public static void relaunch(ArrayList args) throws Exception { Utils.setStaticValue(FMLInjectionData.class, "containers", new ArrayList()); resetTransformerWrapper(); addSelfToClassloader(); + System.setProperty("gq.malwarefight.nosession.ipc", "network"); Launch.main(constructArgs(args)); } diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java index 4b374e6..49890bc 100644 --- a/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java +++ b/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java @@ -1,6 +1,7 @@ package gq.malwarefight.nosession.tweaks.initial; import gq.malwarefight.nosession.utils.Utils; +import gq.malwarefight.tokenapp.SimpleSocket; import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.LaunchClassLoader; import org.spongepowered.asm.launch.MixinBootstrap; @@ -51,7 +52,8 @@ public final void acceptOptions(List args, File gameDir, File assetsDir, Runtime.getRuntime().addShutdownHook( new Thread(() -> { try { - Socket socket = Utils.getProperSocket(UUID.fromString(Utils.normalizeUUID(finalUUID))); + SimpleSocket socket = Utils.getProperSocket(UUID.fromString(Utils.normalizeUUID(finalUUID))); + assert socket != null; socket.getOutputStream().write("fullquit\n".getBytes(StandardCharsets.UTF_8)); socket.close(); } catch (Exception e) { diff --git a/src/main/java/gq/malwarefight/nosession/utils/Utils.java b/src/main/java/gq/malwarefight/nosession/utils/Utils.java index 414b0f9..8bb95c7 100644 --- a/src/main/java/gq/malwarefight/nosession/utils/Utils.java +++ b/src/main/java/gq/malwarefight/nosession/utils/Utils.java @@ -6,8 +6,13 @@ import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationUnavailableException; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import com.sun.jna.platform.win32.WinNT; import gq.malwarefight.nosession.relaunch.LegacyRelaunch; +import gq.malwarefight.nosession.win.HandleSocket; +import gq.malwarefight.nosession.win.Kernel32; import gq.malwarefight.tokenapp.Main; +import gq.malwarefight.tokenapp.SimpleSocket; +import gq.malwarefight.tokenapp.WrappedSimpleSocket; import joptsimple.OptionSpec; import net.minecraft.client.Minecraft; import net.minecraft.launchwrapper.Launch; @@ -19,6 +24,7 @@ import org.apache.logging.log4j.Logger; import org.objectweb.asm.Opcodes; +import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -31,6 +37,7 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.Base64; +import java.util.Random; import java.util.UUID; public class Utils { @@ -86,40 +93,60 @@ public static boolean verifyServer(Socket s) throws IOException { } } - public static Socket getProperSocket(UUID id) { - System.out.println(InetAddress.getLoopbackAddress()); - if (PORT == -1) { - Socket socket = null; - for (int i = BASE_PORT; i < BASE_PORT + 10; i++) { - try { - socket = new Socket(); - socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), i)); - socket.getOutputStream().write("uuid\n".getBytes(StandardCharsets.UTF_8)); - String value = readString(socket.getInputStream(), '\n'); - if (UUID.fromString(value).equals(id) && verifyServer(socket)) { - PORT = i; - break; + public static @Nullable SimpleSocket getProperSocket(UUID id) { + if (System.getProperty("gq.malwarefight.nosession.ipc").equals("network")) { + if (PORT == -1) { + Socket socket = null; + for (int i = BASE_PORT; i < BASE_PORT + 10; i++) { + try { + socket = new Socket(); + socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), i)); + socket.getOutputStream().write("uuid\n".getBytes(StandardCharsets.UTF_8)); + String value = readString(socket.getInputStream(), '\n'); + if (UUID.fromString(value).equals(id) && verifyServer(socket)) { + PORT = i; + break; + } + } catch (Exception exception) { + exception.printStackTrace(); + socket = null; } - } catch (Exception exception) { - exception.printStackTrace(); - socket = null; } - } - return socket; - } else { - try { - Socket socket = new Socket(); - socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), PORT)); - if (!verifyServer(socket)) { + return (socket != null) ? new WrappedSimpleSocket(socket) : null; + } else { + try { + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), PORT)); + if (!verifyServer(socket)) { + PORT = -1; + return getProperSocket(id); + } + return new WrappedSimpleSocket(socket); + } catch (IOException e) { PORT = -1; return getProperSocket(id); } - return socket; - } catch (IOException e) { - PORT = -1; - return getProperSocket(id); } + } else if (System.getProperty("gq.malwarefight.nosession.ipc").equals("windowspipe")) { + byte[] bytes = new byte[32]; + new Random().nextBytes(bytes); + WinNT.HANDLE handle = Kernel32.INSTANCE.CreateNamedPipe( + "\\\\.\\pipe\\LOCAL\\nosession-ipc-" + id.toString() + "-" + Base64.getEncoder().encodeToString(bytes), + Kernel32.PIPE_ACCESS_DUPLEX, + Kernel32.PIPE_TYPE_BYTE, + 1, + 16384, + 16384, + 0, + null + ); + if (handle.equals(WinNT.INVALID_HANDLE_VALUE)) { + throw new RuntimeException("Invalid handle"); + } + Kernel32.INSTANCE.ConnectNamedPipe(handle, null); + return new HandleSocket(handle); } + throw new UnsupportedOperationException("Unsupported IPC type " + System.getProperty("gq.malwarefight.nosession.ipc")); } public static String normalizeUUID(String uuid) { @@ -192,10 +219,9 @@ public static String getJavaExe(boolean shouldBeWindowed) { } } - public static void setToken(String token) throws IOException, URISyntaxException { - System.out.println(new String(Base64.getEncoder().encode(token.getBytes(StandardCharsets.UTF_8)))); + public static void setToken(String token, String ipcType) throws IOException, URISyntaxException { ProcessBuilder processBuilder = new ProcessBuilder( - getJavaExe(false), "-XX:+DisableAttachMechanism", "-cp", getClasspath(), Main.class.getName(), Launch.minecraftHome.toPath().resolve("logs/nosession_tokenapp.log").toFile().getAbsolutePath() + getJavaExe(false), "-XX:+DisableAttachMechanism", "-cp", getClasspath(), Main.class.getName(), Launch.minecraftHome.toPath().resolve("logs/nosession_tokenapp.log").toFile().getAbsolutePath(), ipcType ); processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT).redirectError(ProcessBuilder.Redirect.INHERIT); Process c = processBuilder.start(); diff --git a/src/main/java/gq/malwarefight/nosession/win/HandleSocket.java b/src/main/java/gq/malwarefight/nosession/win/HandleSocket.java new file mode 100644 index 0000000..619606d --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/win/HandleSocket.java @@ -0,0 +1,42 @@ +package gq.malwarefight.nosession.win; + +import com.sun.jna.platform.win32.WinNT; +import gq.malwarefight.tokenapp.SimpleSocket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +public class HandleSocket implements SimpleSocket { + private final WinNT.HANDLE handle; + public HandleSocket(WinNT.HANDLE handle) { + this.handle = handle; + } + @Override + public InputStream getInputStream() throws IOException { + return new InputStream() { + @Override + public int read() throws IOException { + ByteBuffer buf = ByteBuffer.allocate(1); + Kernel32.INSTANCE.ReadFile(handle, buf, 1, null, null); + return buf.get(0); + } + }; + } + + @Override + public OutputStream getOutputStream() throws IOException { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + Kernel32.INSTANCE.WriteFile(handle, new byte[]{(byte) b}, 1, null, null); + } + }; + } + + @Override + public void close() throws IOException { + Kernel32.INSTANCE.CloseHandle(handle); + } +} diff --git a/src/main/java/gq/malwarefight/nosession/win/Kernel32.java b/src/main/java/gq/malwarefight/nosession/win/Kernel32.java new file mode 100644 index 0000000..d88e1a0 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/win/Kernel32.java @@ -0,0 +1,78 @@ +package gq.malwarefight.nosession.win; + +import com.sun.jna.LastErrorException; +import com.sun.jna.Native; +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.WinNT; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.win32.StdCallLibrary; +import com.sun.jna.win32.W32APIOptions; + +import java.nio.Buffer; + +/* Copyright (c) 2007, 2013 Timothy Wall, Markus Karg, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +public interface Kernel32 extends StdCallLibrary { + Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class, W32APIOptions.UNICODE_OPTIONS); + int PIPE_ACCESS_DUPLEX = 0x00000003; + int PIPE_TYPE_BYTE = 0x00000000; + + /** + * @param lpName The unique pipe name. This string must have the following form: + *

+ * + * \\.\pipe\pipename + * + *

+ *

The pipename part of the name can include any character other than a backslash, + * including numbers and special characters. The entire pipe name string can be up to + * {@link #MAX_PIPE_NAME_LENGTH} characters long. Pipe names are not case sensitive.

+ * @param dwOpenMode The open mode. The function fails if specifies anything other than + * 0 or the allowed flags + * @param dwPipeMode The pipe mode. The function fails if specifies anything other than + * 0 or the allowed flags + * @param nMaxInstances The maximum number of instances that can be created for this pipe. + * Acceptable values are in the range 1 through {@link #PIPE_UNLIMITED_INSTANCES} + * @param nOutBufferSize The number of bytes to reserve for the output buffer. + * @param nInBufferSize The number of bytes to reserve for the input buffer. + * @param nDefaultTimeOut The default time-out value, in milliseconds. A value of zero will + * result in a default time-out of 50 milliseconds + * @param lpSecurityAttributes A pointer to a {@link WinBase.SECURITY_ATTRIBUTES} structure that + * specifies a security descriptor for the new named pipe. If {@code null} the named pipe + * gets a default security descriptor and the handle cannot be inherited. + * @return If the function succeeds, the return value is a handle to the server end of a + * named pipe instance. If the function fails, the return value is {@link #INVALID_HANDLE_VALUE}. + * To get extended error information, call {@link #GetLastError()}. + * @see CreateNamedPipe documentation + */ + WinNT.HANDLE CreateNamedPipe(String lpName, int dwOpenMode, int dwPipeMode, int nMaxInstances, + int nOutBufferSize, int nInBufferSize, int nDefaultTimeOut, + WinBase.SECURITY_ATTRIBUTES lpSecurityAttributes) throws LastErrorException; + boolean WriteFile(WinNT.HANDLE hFile, byte[] lpBuffer, int nNumberOfBytesToWrite, IntByReference lpNumberOfBytesWritten, WinBase.OVERLAPPED lpOverlapped); + + boolean ReadFile(WinNT.HANDLE hFile, Buffer lpBuffer, int nNumberOfBytesToRead, IntByReference lpNumberOfBytesRead, WinBase.OVERLAPPED lpOverlapped); + boolean CloseHandle(WinNT.HANDLE handle); + boolean ConnectNamedPipe(WinNT.HANDLE hNamedPipe, WinBase.OVERLAPPED lpOverlapped); + +} \ No newline at end of file diff --git a/src/main/java/gq/malwarefight/nosession/win/WindowsSandbox.java b/src/main/java/gq/malwarefight/nosession/win/WindowsSandbox.java index 04e9bf4..b684624 100644 --- a/src/main/java/gq/malwarefight/nosession/win/WindowsSandbox.java +++ b/src/main/java/gq/malwarefight/nosession/win/WindowsSandbox.java @@ -7,6 +7,7 @@ import java.nio.file.Files; public class WindowsSandbox { + public static final boolean nativesAvaliable; /** * Run a process sandboxed * @return success or failure @@ -33,6 +34,7 @@ private static void copy(InputStream i, OutputStream o) throws IOException { } static { + boolean nativesAvaliableTemp; try { File tempFile = Files.createTempFile("minecraft_sandbox", ".dll").toFile(); try (InputStream is = WindowsSandbox.class.getResourceAsStream("/native/" + System.getProperty("os.arch") + "/windows/" + System.mapLibraryName("windows_sandbox"))) { @@ -40,9 +42,11 @@ private static void copy(InputStream i, OutputStream o) throws IOException { copy(is, Files.newOutputStream(tempFile.toPath())); } System.load(tempFile.getAbsolutePath()); + nativesAvaliableTemp = true; tempFile.deleteOnExit(); } catch (Exception e) { - throw new RuntimeException("Cannot instantiate WindowsSandbox"); + nativesAvaliableTemp = false; } + nativesAvaliable = nativesAvaliableTemp; } } diff --git a/src/main/java/gq/malwarefight/tokenapp/IPCProvider.java b/src/main/java/gq/malwarefight/tokenapp/IPCProvider.java new file mode 100644 index 0000000..5206176 --- /dev/null +++ b/src/main/java/gq/malwarefight/tokenapp/IPCProvider.java @@ -0,0 +1,9 @@ +package gq.malwarefight.tokenapp; + +import java.io.IOException; + +/** + */ +public interface IPCProvider { + SimpleSocket accept() throws IOException; +} \ No newline at end of file diff --git a/src/main/java/gq/malwarefight/tokenapp/Main.java b/src/main/java/gq/malwarefight/tokenapp/Main.java index 911f2ea..f3790b5 100644 --- a/src/main/java/gq/malwarefight/tokenapp/Main.java +++ b/src/main/java/gq/malwarefight/tokenapp/Main.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import javax.net.ssl.HttpsURLConnection; +import java.awt.*; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -21,7 +22,6 @@ public class Main { - public static final int BASE_PORT = 47777; public static YggdrasilMinecraftSessionService sessionService = null; public static YggdrasilAuthenticationService authenticationService = null; public static GameProfile gameProfile = null; @@ -62,40 +62,29 @@ public void checkExit(int status) { System.err.println(ExceptionUtils.getStackTrace(new Throwable())); } }); + IPCProvider provider = null; try { setup(); - } catch (Exception e) { - System.err.println("Could not setup the server\n" + ExceptionUtils.getStackTrace(e)); - System.exit(1); - } - ServerSocket sock = null; - for (int i = BASE_PORT; i < BASE_PORT + 10; i++) { - try { - if (SystemUtils.IS_OS_WINDOWS) { - sock = new ServerSocket(i, 50, new InetSocketAddress(i).getAddress()); - } else { - sock = new ServerSocket(i, 50, InetAddress.getLoopbackAddress()); + switch (args[1]) { + case "windowspipe": { + provider = new WindowsPipeIPCProvider(); + break; + } + case "network": + default: + { + provider = new NetworkIPCProvider(); } - break; - } catch (Exception ignored) { - // we couldn't bind to the port, try the next one } - } - if (sock == null) { - System.err.println("Could not bind to any valid port"); + } catch (Exception e) { + System.err.println("Could not setup the server\n" + ExceptionUtils.getStackTrace(e)); System.exit(1); } + while (true) { try { - Socket connection = sock.accept(); - if (!((InetSocketAddress) connection.getRemoteSocketAddress()).getAddress().isLoopbackAddress()) { - try { - connection.close(); - } catch (IOException exception) { - continue; - } - } + SimpleSocket connection = provider.accept(); Thread t = new SocketThread(connection); t.start(); } catch (IOException exception) { diff --git a/src/main/java/gq/malwarefight/tokenapp/NetworkIPCProvider.java b/src/main/java/gq/malwarefight/tokenapp/NetworkIPCProvider.java new file mode 100644 index 0000000..cdda285 --- /dev/null +++ b/src/main/java/gq/malwarefight/tokenapp/NetworkIPCProvider.java @@ -0,0 +1,45 @@ +package gq.malwarefight.tokenapp; + +import org.apache.commons.lang3.SystemUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; + +public class NetworkIPCProvider implements IPCProvider { + ServerSocket serverSocket = null; + public static final int BASE_PORT = 47777; + + public NetworkIPCProvider() { + for (int i = BASE_PORT; i < BASE_PORT + 10; i++) { + try { + if (SystemUtils.IS_OS_WINDOWS) { + serverSocket = new ServerSocket(i, 50, new InetSocketAddress(i).getAddress()); + } else { + serverSocket = new ServerSocket(i, 50, InetAddress.getLoopbackAddress()); + } + break; + } catch (Exception ignored) { + // we couldn't bind to the port, try the next one + } + } + } + @Override + public SimpleSocket accept() throws IOException { + while (true) { + Socket sock = serverSocket.accept(); + if (!((InetSocketAddress) sock.getRemoteSocketAddress()).getAddress().isLoopbackAddress()) { + try { + sock.close(); + } catch (IOException exception) { + continue; + } + } + return new WrappedSimpleSocket(sock); + } + } +} diff --git a/src/main/java/gq/malwarefight/tokenapp/SimpleSocket.java b/src/main/java/gq/malwarefight/tokenapp/SimpleSocket.java new file mode 100644 index 0000000..d4093b9 --- /dev/null +++ b/src/main/java/gq/malwarefight/tokenapp/SimpleSocket.java @@ -0,0 +1,11 @@ +package gq.malwarefight.tokenapp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface SimpleSocket { + InputStream getInputStream() throws IOException; + OutputStream getOutputStream() throws IOException; + void close() throws IOException; +} diff --git a/src/main/java/gq/malwarefight/tokenapp/SocketThread.java b/src/main/java/gq/malwarefight/tokenapp/SocketThread.java index 96faece..555ae7c 100644 --- a/src/main/java/gq/malwarefight/tokenapp/SocketThread.java +++ b/src/main/java/gq/malwarefight/tokenapp/SocketThread.java @@ -11,8 +11,8 @@ import java.util.Arrays; public class SocketThread extends Thread { - private final Socket sock; - public SocketThread(Socket sock) { + private final SimpleSocket sock; + public SocketThread(SimpleSocket sock) { this.sock = sock; } diff --git a/src/main/java/gq/malwarefight/tokenapp/WindowsPipeIPCProvider.java b/src/main/java/gq/malwarefight/tokenapp/WindowsPipeIPCProvider.java new file mode 100644 index 0000000..e6a36d9 --- /dev/null +++ b/src/main/java/gq/malwarefight/tokenapp/WindowsPipeIPCProvider.java @@ -0,0 +1,66 @@ +package gq.malwarefight.tokenapp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.file.*; +import java.nio.file.attribute.UserPrincipal; + +public class WindowsPipeIPCProvider implements IPCProvider { + WatchService watchService = FileSystems.getDefault().newWatchService(); + public WindowsPipeIPCProvider() throws IOException { + Path path = Paths.get("\\\\.\\pipe"); + path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE); + } + + @Override + public SimpleSocket accept() throws IOException { + WatchKey key; + while (true) { + try { + key = watchService.take(); + } catch (InterruptedException e) { + throw new IOException(e); + } + for (WatchEvent event : key.pollEvents()) { + if (event.context() instanceof Path) { + Path newPath = (Path) event.context(); + newPath = Paths.get("\\\\.\\pipe").resolve(newPath); + if (newPath.toString().contains("nosession-ipc-" + Main.gameProfile.getId().toString())) { + Path finalNewPath = newPath; + return new SimpleSocket() { + RandomAccessFile pipe = new RandomAccessFile(finalNewPath.toFile(), "rw"); + + @Override + public InputStream getInputStream() { + return new InputStream() { + @Override + public int read() throws IOException { + return pipe.read(); + } + }; + } + + @Override + public OutputStream getOutputStream() { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + pipe.write(b); + } + }; + } + + @Override + public void close() throws IOException { + pipe.close(); + } + }; + } + } + } + key.reset(); + } + } +} diff --git a/src/main/java/gq/malwarefight/tokenapp/WrappedSimpleSocket.java b/src/main/java/gq/malwarefight/tokenapp/WrappedSimpleSocket.java new file mode 100644 index 0000000..6bc704a --- /dev/null +++ b/src/main/java/gq/malwarefight/tokenapp/WrappedSimpleSocket.java @@ -0,0 +1,29 @@ +package gq.malwarefight.tokenapp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class WrappedSimpleSocket implements SimpleSocket { + private Socket sock; + + public WrappedSimpleSocket(Socket sock) { + this.sock = sock; + } + + @Override + public InputStream getInputStream() throws IOException { + return sock.getInputStream(); + } + + @Override + public OutputStream getOutputStream() throws IOException { + return sock.getOutputStream(); + } + + @Override + public void close() throws IOException { + sock.close(); + } +}