Skip to content

Commit

Permalink
Working IPC
Browse files Browse the repository at this point in the history
  • Loading branch information
pandaninjas committed Jan 16, 2024
1 parent cc2d41e commit abc3706
Show file tree
Hide file tree
Showing 15 changed files with 385 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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 <noSessionAccessToken>");
args.addAll(Arrays.asList(newArgs.split(" ")));
Expand All @@ -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))
Expand Down Expand Up @@ -180,13 +187,15 @@ 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<String, String>
//noinspection unchecked
builder.addArgs(constructLaunchArgs((HashMap<String, String>) Launch.blackboard.get("launchArgs")));
builder.addArgs("--tweakClass", "net.minecraftforge.fml.common.launcher.FMLTweaker");
builder.build().start();
} else {
Utils.setToken(m.group("token"), "windowspipe");
ArrayList<String> roMounts = new ArrayList<>();
String classPath = rmb.getClassPath();
for (String path : classPath.split(System.getProperty("path.separator"))) {
Expand All @@ -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<String> 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<String, String>
//noinspection unchecked
Expand All @@ -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!");
}
Expand All @@ -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 +(?<token>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")
Expand All @@ -246,22 +255,21 @@ public static void relaunchProcess(Matcher m) throws Exception {
Pattern pattern = Pattern.compile("(?<token>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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -21,7 +20,7 @@ public class YggdrasilSessionMixin {
public void joinServer(GameProfile profile, String authenticationToken, String serverId, CallbackInfo ci) throws AuthenticationException {
try {
if (authenticationToken.equals("<noSessionAccessToken>")) { // 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!");
Expand All @@ -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());
}

Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public static void relaunch(ArrayList<String> args) throws Exception {
Utils.setStaticValue(FMLInjectionData.class, "containers", new ArrayList<String>());
resetTransformerWrapper();
addSelfToClassloader();
System.setProperty("gq.malwarefight.nosession.ipc", "network");
Launch.main(constructArgs(args));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -51,7 +52,8 @@ public final void acceptOptions(List<String> 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) {
Expand Down
86 changes: 56 additions & 30 deletions src/main/java/gq/malwarefight/nosession/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/gq/malwarefight/nosession/win/HandleSocket.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit abc3706

Please sign in to comment.