From 57f02cd5c334ceb6c5e8786b2ced55baba75728d Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Sun, 29 Jan 2023 21:47:46 -0800 Subject: Revert revert commit so we can be back at our original state for this branch (i hope it is the correct branch) This reverts commit 4d5700844809d45ca27a7efe8500d9d4c828ea2f. --- README.md | 1 + SECURITY.md | 2 +- build.gradle | 13 +- gradle/wrapper/gradle-wrapper.jar | Bin 52928 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- .../nosession/NoSessionLoadingPlugin.java | 116 +++++++++++ .../malwarefight/nosession/mixin/BlankTweaker.java | 29 --- .../nosession/mixin/InitialTweaker.java | 209 ------------------- .../gq/malwarefight/nosession/mixin/L2Tweaker.java | 68 ------- .../gq/malwarefight/nosession/mixin/Utils.java | 122 ------------ .../mixin/asm/ReplacingMethodVisitor.java | 22 -- .../mixin/client/YggdrasilSessionMixin.java | 5 +- .../malwarefight/nosession/relaunch/Relaunch.java | 100 ++++++++++ .../nosession/tweaks/CleanupTweaker.java | 82 ++++++++ .../nosession/tweaks/InitialTweaker.java | 104 ++++++++++ .../gq/malwarefight/nosession/utils/Utils.java | 221 +++++++++++++++++++++ src/main/java/gq/malwarefight/tokenapp/Main.java | 2 +- .../gq/malwarefight/tokenapp/SocketThread.java | 2 +- 19 files changed, 639 insertions(+), 463 deletions(-) delete mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java delete mode 100644 src/main/java/gq/malwarefight/nosession/mixin/BlankTweaker.java delete mode 100644 src/main/java/gq/malwarefight/nosession/mixin/InitialTweaker.java delete mode 100644 src/main/java/gq/malwarefight/nosession/mixin/L2Tweaker.java delete mode 100644 src/main/java/gq/malwarefight/nosession/mixin/Utils.java delete mode 100644 src/main/java/gq/malwarefight/nosession/mixin/asm/ReplacingMethodVisitor.java create mode 100644 src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java create mode 100644 src/main/java/gq/malwarefight/nosession/tweaks/CleanupTweaker.java create mode 100644 src/main/java/gq/malwarefight/nosession/tweaks/InitialTweaker.java create mode 100644 src/main/java/gq/malwarefight/nosession/utils/Utils.java diff --git a/README.md b/README.md index c3cab54..925d2a6 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This mod doesn't make you 100% safe, but it makes it much harder to steal your s ## Staying Safe In order to work around an unpatchable security vulnerability, rename the NoSession jar to !.jar so it can load its protection before any other mods.
+You should also use [MultiMC](https://github.com/MultiMC/Launcher/) or one of its derivates [PolyMC](https://github.com/PolyMC/PolyMC) or [PrismLauncher](https://github.com/PrismLauncher/PrismLauncher), because they use launch Minecraft in a way that improves security.
This only protects you from other mods. There are fake verification sites that can steal your session ID through that method.
Don't login with Microsoft OAuth to anything except maybe your Minecraft launcher. You may also want to verify the signature on any NoSession binary. It's signed with [pandaninjas' GPG key](https://raw.githubusercontent.com/pandaninjas/pandaninjas/main/pandaninjas-publickey.key). diff --git a/SECURITY.md b/SECURITY.md index d9d5e6d..c64722e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,7 +7,7 @@ Currently all versions are supported. | Version | Supported | | ------- | ------------------ | | 1.0.0 | ✔️ | - +| Any nightly branch | ❌ | ## Reporting a Vulnerability A vulnerability is currently defined as being able to get the session ID with only a mod that gets loaded after NoSession loads its tweaker.
diff --git a/build.gradle b/build.gradle index e6d4434..5ececca 100644 --- a/build.gradle +++ b/build.gradle @@ -16,10 +16,10 @@ plugins { id "com.github.johnrengelman.shadow" version "2.0.4" } -apply plugin: 'net.minecraftforge.gradle.tweaker-client' +apply plugin: 'net.minecraftforge.gradle.forge' apply plugin: 'org.spongepowered.mixin' -version = "1.0.0" +version = "1.1.0-dev" group = "gq.malwarefight.nosession" archivesBaseName = "nosession" @@ -27,11 +27,10 @@ compileJava.sourceCompatibility = compileJava.targetCompatibility = 1.8 compileJava.options.encoding = "UTF-8" minecraft { - version = "1.8.9" + version = "1.8.9-11.15.1.2318-1.8.9" runDir = "run" mappings = "stable_22" //mappings for 1.8.9 makeObfSourceJar = false //disable creation of sources jar - tweakClass = 'gq.malwarefight.nosession.mixin.InitialTweaker' } configurations { @@ -53,6 +52,8 @@ dependencies { exclude module: 'commons-io' exclude module: 'log4j-core' } + + compileOnly "net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9:universal" } processResources { @@ -90,8 +91,8 @@ jar { "TweakOrder": 0, "ModSide": "CLIENT", 'FMLCorePluginContainsFMLMod': true, - 'TweakClass': 'gq.malwarefight.nosession.mixin.InitialTweaker', - 'MixinConfigs': 'mixins.nosession.json' + 'MixinConfigs': 'mixins.nosession.json', + "FMLCorePlugin": "gq.malwarefight.nosession.NoSessionLoadingPlugin" } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 6ffa237..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7b64320..357df5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip \ No newline at end of file diff --git a/gradlew b/gradlew index 9aa616c..1751d8f 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,5 @@ #!/usr/bin/env bash - +set -x ############################################################################## ## ## Gradle start up script for UN*X diff --git a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java new file mode 100644 index 0000000..3ccbff2 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java @@ -0,0 +1,116 @@ +package gq.malwarefight.nosession; + +import gq.malwarefight.nosession.tweaks.InitialTweaker; +import gq.malwarefight.nosession.utils.Utils; +import net.minecraft.launchwrapper.Launch; +import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@IFMLLoadingPlugin.MCVersion("1.8.9") +@IFMLLoadingPlugin.Name("NoSession trolling") +@IFMLLoadingPlugin.SortingIndex(0) +public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { + @Override + public String[] getASMTransformerClass() { + return new String[0]; + } + + @Override + public String getModContainerClass() { + return null; + } + + @Override + public String getSetupClass() { + return null; + } + + @Override + public void injectData(Map map) { + try { + map.put("coremodLocation", Utils.getLibraryPathAsFile(NoSessionLoadingPlugin.class)); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + @Override + public String getAccessTransformerClass() { + return null; + } + + public static void shutdown() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class shutdown = Class.forName("java.lang.Shutdown"); + Method m = shutdown.getDeclaredMethod("exit", int.class); + m.setAccessible(true); + m.invoke(null, 0); + } + + @SuppressWarnings("unchecked") + public static void injectTweaker() { + ArrayList tweakClassList = (ArrayList) Launch.blackboard.get("TweakClasses"); + tweakClassList.add(0, InitialTweaker.class.getName()); + } + + public static void addSelfToClassLoader() { + Launch.classLoader.addURL(NoSessionLoadingPlugin.class.getProtectionDomain().getCodeSource().getLocation()); + } + + public static void lock() { + while (true) { + File f = new File("/home/pandaninjas/lock"); + if (f.exists()) { + f.delete(); + break; + } + } + } + + static { + System.out.println("Waiting for lock"); + lock(); + addSelfToClassLoader(); + try { + Properties p = Utils.getJavaProperties(); + Pattern mcJWT = Pattern.compile("--accessToken +(?eyJhbGciOiJIUzI1NiJ9\\.[A-Za-z0-9-_]*\\.[A-Za-z0-9-_]*)"); + Matcher m = mcJWT.matcher(p.getProperty("sun.java.command")); + if (m.find()) { + Utils.setToken(m.group("token")); + RuntimeMXBean rmb = ManagementFactory.getRuntimeMXBean(); + ArrayList args = new ArrayList<>(); + args.add(Utils.getJavaExe(p)); + args.add("-cp"); + args.add(p.getProperty("java.class.path")); + args.addAll(rmb.getInputArguments()); + String newArgs = m.replaceAll("--accessToken "); + args.addAll(Arrays.asList(newArgs.split(" "))); + ProcessBuilder processBuilder = new ProcessBuilder( + args.toArray(new String[0]) + ).inheritIO(); + try { + processBuilder.start(); + } catch (IOException e) { + throw new RuntimeException(e); + } + shutdown(); + } + injectTweaker(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/gq/malwarefight/nosession/mixin/BlankTweaker.java b/src/main/java/gq/malwarefight/nosession/mixin/BlankTweaker.java deleted file mode 100644 index 2c9109b..0000000 --- a/src/main/java/gq/malwarefight/nosession/mixin/BlankTweaker.java +++ /dev/null @@ -1,29 +0,0 @@ -package gq.malwarefight.nosession.mixin; - -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.LaunchClassLoader; - -import java.io.File; -import java.util.List; - -public class BlankTweaker implements ITweaker { - @Override - public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - - } - - @Override - public void injectIntoClassLoader(LaunchClassLoader classLoader) { - - } - - @Override - public String getLaunchTarget() { - return null; - } - - @Override - public String[] getLaunchArguments() { - return new String[0]; - } -} diff --git a/src/main/java/gq/malwarefight/nosession/mixin/InitialTweaker.java b/src/main/java/gq/malwarefight/nosession/mixin/InitialTweaker.java deleted file mode 100644 index 51425fc..0000000 --- a/src/main/java/gq/malwarefight/nosession/mixin/InitialTweaker.java +++ /dev/null @@ -1,209 +0,0 @@ -package gq.malwarefight.nosession.mixin; - -import com.google.common.annotations.Beta; -import com.google.common.collect.ForwardingMultimap; -import com.google.gson.Gson; -import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.Launch; -import net.minecraft.launchwrapper.LaunchClassLoader; -import net.minecraft.launchwrapper.LogWrapper; -import org.apache.commons.io.ByteOrderMark; -import org.apache.commons.lang3.CharEncoding; -import org.apache.commons.lang3.Validate; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.launch.MixinBootstrap; -import org.spongepowered.asm.mixin.MixinEnvironment; -import org.spongepowered.asm.mixin.Mixins; - -import java.io.File; -import java.io.IOException; -import java.net.Socket; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -@SuppressWarnings({"unused", "unchecked"}) -public class InitialTweaker implements ITweaker { - - // I'm creating my own allTweakers list - // with blackjack and hookers! - public final ArrayList allTweakers = new ArrayList<>(); - - public InitialTweaker() {} - - public String getLibraryPath(Class c) throws URISyntaxException { - return new File(c.getProtectionDomain().getCodeSource().getLocation().toURI()).getAbsolutePath(); - } - - public String getClasspath() throws URISyntaxException { - return String.join( - System.getProperty("path.separator"), - getLibraryPath(InitialTweaker.class), - getLibraryPath(YggdrasilAuthenticationService.class), - getLibraryPath(Gson.class), - getLibraryPath(LogManager.class), - getLibraryPath(Validate.class), - getLibraryPath(ForwardingMultimap.class), - getLibraryPath(Beta.class), - getLibraryPath(CharEncoding.class), - getLibraryPath(ByteOrderMark.class), - getLibraryPath(Logger.class), - getLibraryPath(Opcodes.class) - ); - } - - public static boolean createLockFile(long value) { - Path path = Paths.get(System.getProperty("java.io.tmpdir"), "NoSessionLock" + value); - try { - Files.createFile(path); - } catch (FileAlreadyExistsException e) { - System.out.println("You won the lottery! Two NoSession instances used the same ID. The chance that a new NoSession instance uses the same ID as an old one is (NoSession instances) / 2^64"); - return false; - } catch (IOException e) { - LogManager.getLogger().warn("Failed to create lockfile " + e.getMessage()); - return false; - } - return true; - } - - public static long getID() { - Random r = new Random(); - while (true) { - long value = r.nextLong(); - if (createLockFile(value)) { - Runtime.getRuntime().addShutdownHook( - new Thread(() -> { - try { - Files.delete(Paths.get(System.getProperty("java.io.tmpdir"), "NoSessionLock" + value)); - } catch (Exception ignored) {} - }) - ); - return value; - } - } - } - - public void setToken(String token) throws IOException, URISyntaxException { - long value = getID(); - Utils.ID = value; - File javaExecutable = Paths.get(System.getProperty("java.home"), "bin", "java").toFile(); - ProcessBuilder processBuilder = new ProcessBuilder( - javaExecutable.getAbsolutePath(), "-cp", getClasspath(), "gq.malwarefight.tokenapp.Main", Long.toString(value) - ); - processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT).redirectError(ProcessBuilder.Redirect.INHERIT); - Process c = processBuilder.start(); - c.getOutputStream().write((token + "\n").getBytes(StandardCharsets.UTF_8)); - c.getOutputStream().flush(); - Runtime.getRuntime().addShutdownHook( - new Thread(() -> { - try { - Socket socket = Utils.getProperSocket(); - socket.getOutputStream().write("fullquit\n".getBytes(StandardCharsets.UTF_8)); - socket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - }) - ); - } - - /** - * This handles the launch arguments passed towards minecraft - * @param args The launch arguments - * @param gameDir The game directory (ie: .minecraft) - * @param assetsDir The assets directory - * @param profile The game profile - */ - @Override - @SuppressWarnings("unchecked") - public final void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - ArrayList argsCopy = new ArrayList<>(args); - for (int i = 0; i < argsCopy.size(); i++) { - if (argsCopy.get(i).equals("--accessToken")) { - try { - setToken(args.get(i + 1)); - } catch (IOException | URISyntaxException e) { - throw new RuntimeException(e); - } - argsCopy.set(i + 1, ""); - } - } - boolean any = false; - // in order to prevent other mods from seeing the true list of args with the token - // we will just call all the other tweakers from here - ArrayList tweakers = (ArrayList) Launch.blackboard.get("Tweaks"); - for (final ITweaker tweaker : tweakers) { - if (tweaker == this) - continue; - - LogWrapper.log(Level.INFO, "Calling tweak class %s", tweaker.getClass().getName()); - tweaker.acceptOptions(argsCopy, gameDir, assetsDir, profile); - tweaker.injectIntoClassLoader(Launch.classLoader); - allTweakers.add(tweaker); - tweakers.set(tweakers.indexOf(tweaker), new BlankTweaker()); - any = true; - } - if (any) { - ((ArrayList) Launch.blackboard.get("TweakClasses")).add(0, - Utils.getUniqueClassName() - ); - } - } - - /** - * Inject into the MC class loader - * @param classLoader The class loader - */ - @Override - public final void injectIntoClassLoader(LaunchClassLoader classLoader) { - MixinBootstrap.init(); - MixinEnvironment environment = MixinEnvironment.getDefaultEnvironment(); - Mixins.addConfiguration("mixins.nosession.json"); - // Check if the obfuscation context is null - if (environment.getObfuscationContext() == null) { - environment.setObfuscationContext("notch"); - } - // This is a client side, client :) - environment.setSide(MixinEnvironment.Side.CLIENT); - } - - @Override - public String getLaunchTarget() { - return MixinBootstrap.getPlatform().getLaunchTarget(); - } - - @Override - public String[] getLaunchArguments() { - - ArrayList argsFromOurTweakers = new ArrayList<>(); - - for (ITweaker i: allTweakers) { - argsFromOurTweakers.addAll(Arrays.asList(i.getLaunchArguments())); - } - - //noinspection unchecked - ArrayList launchArgs = (ArrayList) Launch.blackboard.get("ArgumentList"); - for (int i = 0; i < launchArgs.size(); i++) { - if (launchArgs.get(i).equals("--accessToken")) { - launchArgs.set(i + 1, ""); - } - } - if (!launchArgs.contains("--accessToken")) { - argsFromOurTweakers.add("--accessToken"); - argsFromOurTweakers.add(""); - } - - return argsFromOurTweakers.toArray(new String[0]); - } -} diff --git a/src/main/java/gq/malwarefight/nosession/mixin/L2Tweaker.java b/src/main/java/gq/malwarefight/nosession/mixin/L2Tweaker.java deleted file mode 100644 index 8736ca4..0000000 --- a/src/main/java/gq/malwarefight/nosession/mixin/L2Tweaker.java +++ /dev/null @@ -1,68 +0,0 @@ -package gq.malwarefight.nosession.mixin; - -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.Launch; -import net.minecraft.launchwrapper.LaunchClassLoader; -import net.minecraft.launchwrapper.LogWrapper; -import org.apache.logging.log4j.Level; -import org.spongepowered.asm.launch.MixinBootstrap; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -@SuppressWarnings("unused") // is used, but not mentioned directly -public class L2Tweaker implements ITweaker { - - public final ArrayList allTweakers = new ArrayList<>(); - - @Override - @SuppressWarnings("unchecked") - public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - ArrayList argsCopy = new ArrayList<>(args); - for (int i = 0; i < argsCopy.size(); i++) { - if (argsCopy.get(i).equals("--accessToken")) { - argsCopy.set(i + 1, ""); - } - } - - boolean any = false; - ArrayList tweakers = (ArrayList) Launch.blackboard.get("Tweaks"); - // in order to prevent other mods from seeing the true list of args with the token - // we will just call all the other tweakers from here - for (final ITweaker tweaker : tweakers) { - if (tweaker == this) - continue; - LogWrapper.log(Level.INFO, "Calling tweak class %s", tweaker.getClass().getName()); - tweaker.acceptOptions(argsCopy, gameDir, assetsDir, profile); - tweaker.injectIntoClassLoader(Launch.classLoader); - allTweakers.add(tweaker); - tweakers.set(tweakers.indexOf(tweaker), new BlankTweaker()); - any = true; - } - if (any) { - ((ArrayList) Launch.blackboard.get("TweakClasses")).add(0, - Utils.getUniqueClassName() - ); - } - } - - @Override - public void injectIntoClassLoader(LaunchClassLoader classLoader) {} - - @Override - public String getLaunchTarget() { - return MixinBootstrap.getPlatform().getLaunchTarget(); - } - - @Override - public String[] getLaunchArguments() { - ArrayList argsFromOurTweakers = new ArrayList<>(); - - for (ITweaker i: allTweakers) { - argsFromOurTweakers.addAll(Arrays.asList(i.getLaunchArguments())); - } - return argsFromOurTweakers.toArray(new String[0]); - } -} \ No newline at end of file diff --git a/src/main/java/gq/malwarefight/nosession/mixin/Utils.java b/src/main/java/gq/malwarefight/nosession/mixin/Utils.java deleted file mode 100644 index 2aaec37..0000000 --- a/src/main/java/gq/malwarefight/nosession/mixin/Utils.java +++ /dev/null @@ -1,122 +0,0 @@ -package gq.malwarefight.nosession.mixin; - -import gq.malwarefight.nosession.mixin.asm.ReplacingMethodVisitor; -import net.minecraft.launchwrapper.Launch; -import org.objectweb.asm.*; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -public class Utils { - static int num = 0; - static int PORT = -1; - static long ID = -1; - private static final int BASE_PORT = 47777; - - public static byte[] read(InputStream i, Character delimiter) throws IOException { - byte[] buffer = new byte[512]; - int index = 0; - while (true) { - int in = i.read(); - if (in == -1 || (delimiter != null && delimiter == in)) { - return Arrays.copyOfRange(buffer, 0, index); - } - if (index == buffer.length) { - // grow the buffer - byte[] newBuffer = new byte[buffer.length * 2]; - System.arraycopy( - buffer, 0, newBuffer, 0, buffer.length - ); - buffer = newBuffer; - } - buffer[index] = (byte) in; - index++; - } - } - - public static String readString(InputStream i, Character delimiter) throws IOException { - return new String(read(i, delimiter), StandardCharsets.UTF_8); - } - - public static void createClass(byte[] classArray, String name) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Method m = ClassLoader.class.getDeclaredMethod( - "defineClass", String.class, byte[].class, int.class, int.class); - m.setAccessible(true); - m.invoke(Launch.classLoader, name, classArray, 0, classArray.length); - } - - public static String getUniqueClassName() { - - String className = "gq.malwarefight.nosession.mixin.L2TweakerClone" + num; - - byte[] L2TweakerBytes; - try { - //noinspection ConstantConditions - L2TweakerBytes = read(Utils.class.getResourceAsStream("/gq/malwarefight/nosession/mixin/L2Tweaker.class"), null); - } catch (IOException e) { - throw new RuntimeException(e); - } - - ClassReader cr = new ClassReader(L2TweakerBytes); - ClassWriter cw = new ClassWriter(cr, 0); - cr.accept(new ClassVisitor(Opcodes.ASM5, cw) { - @Override - public void visit(int version, int access, String name, - String signature, String superName, String[] interfaces) { - super.visit(version, access, className.replace(".", "/"), signature, superName, interfaces); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, - String signature, String[] exceptions) { - return new ReplacingMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions), L2Tweaker.class.getName().replace(".", "/"), className.replace(".", "/")); - } - }, 0); - - byte[] code = cw.toByteArray(); - try { - createClass(code, className); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - num++; - return className; - } - - public static Socket getProperSocket() { - if (PORT == -1) { - Socket socket = null; - int port = 0; - for (int i = BASE_PORT; i < BASE_PORT + 10; i++) { - try { - socket = new Socket(); - socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), i)); - socket.getOutputStream().write("id\n".getBytes(StandardCharsets.UTF_8)); - String value = readString(socket.getInputStream(), '\n'); - if (value.equals(Long.toString(ID))) { - port = i; - break; - } - } catch (Exception ignored) {} - } - PORT = port; - return socket; - } else { - try { - Socket socket = new Socket(); - socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), PORT)); - return socket; - } catch (IOException e) { - PORT = -1; - return getProperSocket(); - } - } - } -} diff --git a/src/main/java/gq/malwarefight/nosession/mixin/asm/ReplacingMethodVisitor.java b/src/main/java/gq/malwarefight/nosession/mixin/asm/ReplacingMethodVisitor.java deleted file mode 100644 index d8c3005..0000000 --- a/src/main/java/gq/malwarefight/nosession/mixin/asm/ReplacingMethodVisitor.java +++ /dev/null @@ -1,22 +0,0 @@ -package gq.malwarefight.nosession.mixin.asm; - -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -public class ReplacingMethodVisitor extends MethodVisitor implements Opcodes { - - final String newName; - final String oldName; - public ReplacingMethodVisitor(MethodVisitor mv, String oldName, String newName) { - super(ASM5, mv); - this.oldName = oldName; - this.newName = newName; - } - - public void visitFieldInsn(int opcode, String owner, String name, - String desc) { - owner = owner.replace(oldName, newName); - super.visitFieldInsn(opcode, owner, name, desc); - } - -} 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 48327fe..5451c26 100644 --- a/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java +++ b/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java @@ -1,9 +1,10 @@ package gq.malwarefight.nosession.mixin.client; +import com.google.common.primitives.Booleans; import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; -import gq.malwarefight.nosession.mixin.Utils; +import gq.malwarefight.nosession.utils.Utils; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -46,4 +47,4 @@ public class YggdrasilSessionMixin { } catch (IOException ignored) {} } } -} +} \ No newline at end of file diff --git a/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java b/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java new file mode 100644 index 0000000..55e907c --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java @@ -0,0 +1,100 @@ +package gq.malwarefight.nosession.relaunch; + +import gq.malwarefight.nosession.tweaks.CleanupTweaker; +import gq.malwarefight.nosession.utils.Utils; +import net.minecraft.launchwrapper.Launch; +import net.minecraftforge.fml.client.FMLClientHandler; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModAPIManager; +import net.minecraftforge.fml.common.asm.ASMTransformerWrapper; +import net.minecraftforge.fml.common.registry.ItemStackHolderInjector; +import net.minecraftforge.fml.common.registry.ObjectHolderRegistry; +import net.minecraftforge.fml.relauncher.FMLInjectionData; +import net.minecraftforge.fml.relauncher.FMLLaunchHandler; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; + +public class Relaunch { + public static void relaunch(ArrayList args, File gameDir, File assetsDir, String version) throws Exception { + resetSecurityManager(); + setToNull(FMLLaunchHandler.class, "INSTANCE"); + setToNull(Loader.class, "instance"); + setToNull(ModAPIManager.class, "INSTANCE"); + setToNull(ObjectHolderRegistry.class, "INSTANCE"); + setToNull(ItemStackHolderInjector.class, "INSTANCE"); + setToNull(FMLClientHandler.class, "INSTANCE"); + setToNull(Loader.class, "injectedContainers"); + Utils.setStaticValue(FMLInjectionData.class, "containers", new ArrayList()); + resetClass(ASMTransformerWrapper.class); + URLClassLoader originalClassLoader = (URLClassLoader) Launch.class.getClassLoader(); + URL[] newURLS = new URL[originalClassLoader.getURLs().length + 1]; + URLClassLoader lcl = new URLClassLoader(newURLS, originalClassLoader); + //noinspection unchecked + Class innerLaunch = (Class) Class.forName("net.minecraft.launchwrapper.Launch", false, lcl); + Method launch = innerLaunch.getDeclaredMethod("main", String[].class); + launch.invoke(null, (Object) constructArgs(args, gameDir, assetsDir, version)); + } + + public static String[] constructArgs(ArrayList initial, File gameDir, File assetDir, String version) { + initial.add("--version"); + initial.add(version); + initial.add("--gameDir"); + initial.add(gameDir.getAbsolutePath()); + initial.add("--assetsDir"); + initial.add(assetDir.getAbsolutePath()); + initial.add("--tweakClass"); + initial.add("gq.malwarefight.nosession.tweaks.CleanupTweaker"); + return initial.toArray(new String[0]); + } + + public static void resetSecurityManager() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Method m = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); + m.setAccessible(true); + Field[] fields = (Field[]) m.invoke(System.class, false); + for (Field field: fields) { + if (field.getType().equals(SecurityManager.class)) { + field.setAccessible(true); + field.set(null, null); + } + } + } + + + public static void resetClass(Class cls) throws IllegalAccessException, NoSuchFieldException { + Field[] fields = cls.getDeclaredFields(); + for (Field field: fields) { + if ((field.getModifiers() & Modifier.STATIC) != 0) { + setToNull(field); + } + } + } + + public static void setToNull(Class cls, String fieldname) throws NoSuchFieldException, IllegalAccessException { + Field f = cls.getDeclaredField(fieldname); + setToNull(f); + } + + public static void setToNull(Field f) throws IllegalAccessException, NoSuchFieldException { + f.setAccessible(true); + if ((f.getModifiers() & Modifier.FINAL) != 0) { // if it is final + Field modifiers = Field.class.getDeclaredField("modifiers"); + modifiers.setAccessible(true); + int value = modifiers.getInt(f); + value &= ~Modifier.FINAL; + modifiers.setInt(f, value); + } + if (f.getGenericType().equals(Boolean.TYPE)) { + f.set(null, false); + } else { + f.set(null, null); + } + } + +} diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/CleanupTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/CleanupTweaker.java new file mode 100644 index 0000000..6deeeb0 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/tweaks/CleanupTweaker.java @@ -0,0 +1,82 @@ +package gq.malwarefight.nosession.tweaks; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; +import net.minecraftforge.fml.common.asm.ASMTransformerWrapper; + +import java.io.File; +import java.lang.reflect.*; +import java.net.URL; +import java.net.URLStreamHandler; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import gq.malwarefight.nosession.utils.Utils; + +public class CleanupTweaker implements ITweaker { + /** + * Cached to avoid expensive reflection operations + */ + public Method makeWrapper; + + public CleanupTweaker() throws NoSuchMethodException { + makeWrapper = ASMTransformerWrapper.class.getDeclaredMethod("makeWrapper", String.class); + makeWrapper.setAccessible(true); + } + @Override + @SuppressWarnings("unchecked") + public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { + // add the FMLTweaker + ArrayList tweakerList = (ArrayList) Launch.blackboard.get("TweakClasses"); + tweakerList.add("net.minecraftforge.fml.common.launcher.FMLTweaker"); + // reset ASMTransformerWrapper + Class cls = ASMTransformerWrapper.class; + try { + Utils.setStaticValue(cls, "wrapperModMap", new HashMap()); + Utils.setStaticValue(cls, "wrapperParentMap", new HashMap()); + Utils.setStaticValue(cls, "wrapperCache", CacheBuilder.newBuilder().maximumSize(30L).weakValues().build(new CacheLoader() { + public byte[] load(String file) throws Exception { + return (byte[]) makeWrapper.invoke(null, file); + } + })); + Utils.setStaticValue(cls, "asmGenRoot", new URL("asmgen", null, -1, "/", getAsmGenHandler())); + Utils.setStaticValue(cls, "injected", false); + } catch (Exception e) { + System.err.println("NoSession: Fixing ASMTransformerWrapper failed. Things may break."); + e.printStackTrace(); + } + } + + @Override + public void injectIntoClassLoader(LaunchClassLoader classLoader) {} + + @Override + public String getLaunchTarget() { + return "net.minecraft.client.main.Main"; + } + + @Override + public String[] getLaunchArguments() { + return new String[0]; + } + + + + @SuppressWarnings("unchecked") + private URLStreamHandler getAsmGenHandler() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Class cls = ASMTransformerWrapper.class; + for (Class candidate: cls.getDeclaredClasses()) { + if (candidate.getSimpleName().equals("ASMGenHandler")) { + Method pgdc = Class.class.getDeclaredMethod("privateGetDeclaredConstructors", boolean.class); + pgdc.setAccessible(true); + Constructor constructor = ((Constructor[]) pgdc.invoke(candidate, false))[0]; + constructor.setAccessible(true); + return constructor.newInstance(); + } + } + throw new RuntimeException("NoSession: unable to get the ASMGenHandler"); + } +} diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/InitialTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/InitialTweaker.java new file mode 100644 index 0000000..74d05f5 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/tweaks/InitialTweaker.java @@ -0,0 +1,104 @@ +package gq.malwarefight.nosession.tweaks; + +import gq.malwarefight.nosession.relaunch.Relaunch; +import gq.malwarefight.nosession.utils.Utils; +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; +import org.spongepowered.asm.launch.MixinBootstrap; +import org.spongepowered.asm.mixin.MixinEnvironment; +import org.spongepowered.asm.mixin.Mixins; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.Socket; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class InitialTweaker implements ITweaker { + + public InitialTweaker() { + System.gc(); // try to garbage collect the earlier launch data + } + + /** + * This handles the launch arguments passed towards minecraft + * @param args The launch arguments + * @param gameDir The game directory (ie: .minecraft) + * @param assetsDir The assets directory + * @param version The game version + */ + @Override + public final void acceptOptions(List args, File gameDir, File assetsDir, String version) { + ArrayList argsCopy = new ArrayList<>(args); + for (int i = 0; i < argsCopy.size(); i++) { + if (argsCopy.get(i).equals("--accessToken")) { + if (argsCopy.get(i + 1).equals("")) { + Runtime.getRuntime().addShutdownHook( + new Thread(() -> { + try { + Socket socket = Utils.getProperSocket(); + socket.getOutputStream().write("fullquit\n".getBytes(StandardCharsets.UTF_8)); + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + }) + ); + return; // don't do anything, the change has already been made + } + try { + Utils.setToken(args.get(i + 1)); + } catch (Exception e) { + throw new RuntimeException(e); + } + argsCopy.set(i + 1, ""); + } + } + System.out.println("======================="); + System.out.println("NoSession: relaunching without the token"); + System.out.println("======================="); + try { + Relaunch.relaunch(argsCopy, gameDir, assetsDir, version); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + + } + + /** + * Inject into the MC class loader + * @param classLoader The class loader + */ + @Override + public final void injectIntoClassLoader(LaunchClassLoader classLoader) { + MixinBootstrap.init(); + MixinEnvironment environment = MixinEnvironment.getDefaultEnvironment(); + Mixins.addConfiguration("mixins.nosession.json"); + // Check if the obfuscation context is null + if (environment.getObfuscationContext() == null) { + environment.setObfuscationContext("notch"); + } + // This is a client side, client :) + environment.setSide(MixinEnvironment.Side.CLIENT); + } + + @Override + public String getLaunchTarget() { + return MixinBootstrap.getPlatform().getLaunchTarget(); + } + + @Override + public String[] getLaunchArguments() { + return new String[0]; + } + +} diff --git a/src/main/java/gq/malwarefight/nosession/utils/Utils.java b/src/main/java/gq/malwarefight/nosession/utils/Utils.java new file mode 100644 index 0000000..fbacb8f --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/utils/Utils.java @@ -0,0 +1,221 @@ +package gq.malwarefight.nosession.utils; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ForwardingMultimap; +import com.google.gson.Gson; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import gq.malwarefight.nosession.tweaks.InitialTweaker; +import gq.malwarefight.tokenapp.Main; +import org.apache.commons.io.ByteOrderMark; +import org.apache.commons.lang3.CharEncoding; +import org.apache.commons.lang3.SystemUtils; +import org.apache.commons.lang3.Validate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.Opcodes; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Properties; +import java.util.Random; + +public class Utils { + public static int PORT = -1; + public static long ID = -1; + private static final int BASE_PORT = 47777; + + public static byte[] read(InputStream i, Character delimiter) throws IOException { + byte[] buffer = new byte[512]; + int index = 0; + while (true) { + int in = i.read(); + if (in == -1 || (delimiter != null && delimiter == in)) { + return Arrays.copyOfRange(buffer, 0, index); + } + if (index == buffer.length) { + // grow the buffer + byte[] newBuffer = new byte[buffer.length * 2]; + System.arraycopy( + buffer, 0, newBuffer, 0, buffer.length + ); + buffer = newBuffer; + } + buffer[index] = (byte) in; + index++; + } + } + + public static String readString(InputStream i, Character delimiter) throws IOException { + return new String(read(i, delimiter), StandardCharsets.UTF_8); + } + + public static Socket getProperSocket() { + if (PORT == -1) { + Socket socket = null; + int port = 0; + for (int i = BASE_PORT; i < BASE_PORT + 10; i++) { + try { + socket = new Socket(); + socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), i)); + socket.getOutputStream().write("id\n".getBytes(StandardCharsets.UTF_8)); + String value = readString(socket.getInputStream(), '\n'); + if (value.equals(Long.toString(ID))) { + port = i; + break; + } + } catch (Exception ignored) {} + } + PORT = port; + return socket; + } else { + try { + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), PORT)); + return socket; + } catch (IOException e) { + PORT = -1; + return getProperSocket(); + } + } + } + + public static void setStaticValue(Class cls, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { + Field f = cls.getDeclaredField(fieldName); + f.setAccessible(true); + if ((f.getModifiers() & Modifier.FINAL) != 0) { // if it is final + Field modifiers = Field.class.getDeclaredField("modifiers"); + modifiers.setAccessible(true); + int modifiersValue = modifiers.getInt(f); + modifiersValue &= ~Modifier.FINAL; + modifiers.setInt(f, modifiersValue); + } + f.set(null, value); + } + +// public static String processString(String s) { +// if (s.lastIndexOf("!") == -1) { +// return s; +// } +// } + + public static File getLibraryPathAsFile(Class c) throws URISyntaxException { + return new File(c.getProtectionDomain().getCodeSource().getLocation().getPath()); + } + + public static String getLibraryPath(Class c) throws URISyntaxException { + + return getLibraryPathAsFile(c).getAbsolutePath(); + } + + public static String getClasspath(Properties p) throws URISyntaxException { + try { + // try to be smart + return String.join( + p.getProperty("path.separator"), + getLibraryPath(Utils.class), + getLibraryPath(YggdrasilAuthenticationService.class), + getLibraryPath(Gson.class), + getLibraryPath(LogManager.class), + getLibraryPath(Validate.class), + getLibraryPath(ForwardingMultimap.class), + getLibraryPath(Beta.class), + getLibraryPath(CharEncoding.class), + getLibraryPath(ByteOrderMark.class), + getLibraryPath(Logger.class), + getLibraryPath(Opcodes.class) + ); + } catch (URISyntaxException | IllegalArgumentException e) { + e.printStackTrace(); + // fallback to "dumb" method + RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + return runtimeMXBean.getClassPath() + p.getProperty("path.separator") + getLibraryPath(Utils.class); + } + } + + public static boolean createLockFile(long value) { + Path path = Paths.get(System.getProperty("java.io.tmpdir"), "NoSessionLock" + value); + try { + Files.createFile(path); + } catch (FileAlreadyExistsException e) { + LogManager.getLogger().info("You won the lottery! Two NoSession instances used the same ID. The chance that a new NoSession instance uses the same ID as an old one is (NoSession instances) / 2^64"); + return false; + } catch (IOException e) { + LogManager.getLogger().warn("Failed to create lockfile " + e.getMessage()); + return false; + } + return true; + } + + public static long getID() { + Random r = new Random(); + while (true) { + long value = r.nextLong(); + if (createLockFile(value)) { + Runtime.getRuntime().addShutdownHook( + new Thread(() -> { + try { + Files.delete(Paths.get(System.getProperty("java.io.tmpdir"), "NoSessionLock" + value)); + } catch (Exception ignored) {} + }) + ); + return value; + } + } + } + + public static Properties getJavaProperties() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Properties p = new Properties(); + Method m = System.class.getDeclaredMethod("initProperties", Properties.class); + m.setAccessible(true); + m.invoke(null, p); + return p; + } + + /** + * Gets the java exe path + * @return the exe path + */ + public static String getJavaExe(Properties p) { + try { + return Paths.get(String.join( + p.getProperty("file.separator"), + p.getProperty("java.home"), + "bin", + "java" + (SystemUtils.IS_OS_WINDOWS ? ".exe" : "") + )).toFile().getAbsolutePath(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void setToken(String token) throws IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, URISyntaxException { + long value = getID(); + ID = value; + Properties p = getJavaProperties(); + System.out.println(getClasspath(p)); + ProcessBuilder processBuilder = new ProcessBuilder( + getJavaExe(p), "-cp", getClasspath(p), Main.class.getName(), Long.toString(value) + ); + processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT).redirectError(ProcessBuilder.Redirect.INHERIT); + Process c = processBuilder.start(); + c.getOutputStream().write((token + "\n").getBytes(StandardCharsets.UTF_8)); + c.getOutputStream().flush(); + } +} diff --git a/src/main/java/gq/malwarefight/tokenapp/Main.java b/src/main/java/gq/malwarefight/tokenapp/Main.java index 66afad1..62a4da3 100644 --- a/src/main/java/gq/malwarefight/tokenapp/Main.java +++ b/src/main/java/gq/malwarefight/tokenapp/Main.java @@ -5,7 +5,7 @@ import com.google.gson.JsonParser; import com.mojang.authlib.GameProfile; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; -import gq.malwarefight.nosession.mixin.Utils; +import gq.malwarefight.nosession.utils.Utils; import org.apache.commons.lang3.exception.ExceptionUtils; import javax.net.ssl.HttpsURLConnection; diff --git a/src/main/java/gq/malwarefight/tokenapp/SocketThread.java b/src/main/java/gq/malwarefight/tokenapp/SocketThread.java index 05db6f0..de029c1 100644 --- a/src/main/java/gq/malwarefight/tokenapp/SocketThread.java +++ b/src/main/java/gq/malwarefight/tokenapp/SocketThread.java @@ -1,7 +1,7 @@ package gq.malwarefight.tokenapp; import com.mojang.authlib.exceptions.AuthenticationException; -import gq.malwarefight.nosession.mixin.Utils; +import gq.malwarefight.nosession.utils.Utils; import java.io.IOException; import java.io.InputStream; -- cgit From fd51baf3f06141a151fde7ad1ecdd80b8093351f Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Wed, 1 Feb 2023 11:27:18 -0800 Subject: Amogus does not work --- build.gradle | 2 +- .../nosession/NoSessionLoadingPlugin.java | 8 +-- .../gq/malwarefight/nosession/utils/Utils.java | 72 +++++++++++++--------- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/build.gradle b/build.gradle index 5ececca..d7a1031 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ plugins { apply plugin: 'net.minecraftforge.gradle.forge' apply plugin: 'org.spongepowered.mixin' -version = "1.1.0-dev" +project.version = "1.1.0-dev" group = "gq.malwarefight.nosession" archivesBaseName = "nosession" diff --git a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java index 3ccbff2..0ceff30 100644 --- a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java +++ b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java @@ -39,13 +39,7 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { } @Override - public void injectData(Map map) { - try { - map.put("coremodLocation", Utils.getLibraryPathAsFile(NoSessionLoadingPlugin.class)); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } + public void injectData(Map map) {} @Override public String getAccessTransformerClass() { diff --git a/src/main/java/gq/malwarefight/nosession/utils/Utils.java b/src/main/java/gq/malwarefight/nosession/utils/Utils.java index fbacb8f..ae0e273 100644 --- a/src/main/java/gq/malwarefight/nosession/utils/Utils.java +++ b/src/main/java/gq/malwarefight/nosession/utils/Utils.java @@ -109,43 +109,58 @@ public class Utils { f.set(null, value); } -// public static String processString(String s) { -// if (s.lastIndexOf("!") == -1) { -// return s; -// } -// } - - public static File getLibraryPathAsFile(Class c) throws URISyntaxException { - return new File(c.getProtectionDomain().getCodeSource().getLocation().getPath()); + public static String processString(String uri) { + try { + return uri.substring(uri.indexOf('/'), uri.lastIndexOf('!')); + } catch (Exception e) { + e.printStackTrace(); + return uri; + } } - public static String getLibraryPath(Class c) throws URISyntaxException { - - return getLibraryPathAsFile(c).getAbsolutePath(); + public static String getLibraryPath(Class c, boolean processString) throws URISyntaxException { + String uri = c.getProtectionDomain().getCodeSource().getLocation().toURI().toString(); + if (processString) { + uri = processString(uri); + } + return Paths.get(uri).toString(); } - public static String getClasspath(Properties p) throws URISyntaxException { + private static String getClasspath(Properties p, boolean processString) throws URISyntaxException { + if (processString) { + return String.join( + p.getProperty("path.separator"), + getLibraryPath(Utils.class, true), + getLibraryPath(YggdrasilAuthenticationService.class, true), + getLibraryPath(Gson.class, true), + getLibraryPath(LogManager.class, true), + getLibraryPath(Validate.class, true), + getLibraryPath(ForwardingMultimap.class, true), + getLibraryPath(Beta.class, true), + getLibraryPath(CharEncoding.class, true), + getLibraryPath(ByteOrderMark.class, true), + getLibraryPath(Logger.class, true), + getLibraryPath(Opcodes.class, true) + ); + } try { // try to be smart return String.join( p.getProperty("path.separator"), - getLibraryPath(Utils.class), - getLibraryPath(YggdrasilAuthenticationService.class), - getLibraryPath(Gson.class), - getLibraryPath(LogManager.class), - getLibraryPath(Validate.class), - getLibraryPath(ForwardingMultimap.class), - getLibraryPath(Beta.class), - getLibraryPath(CharEncoding.class), - getLibraryPath(ByteOrderMark.class), - getLibraryPath(Logger.class), - getLibraryPath(Opcodes.class) + getLibraryPath(Utils.class, false), + getLibraryPath(YggdrasilAuthenticationService.class, false), + getLibraryPath(Gson.class, false), + getLibraryPath(LogManager.class, false), + getLibraryPath(Validate.class, false), + getLibraryPath(ForwardingMultimap.class, false), + getLibraryPath(Beta.class, false), + getLibraryPath(CharEncoding.class, false), + getLibraryPath(ByteOrderMark.class, false), + getLibraryPath(Logger.class, false), + getLibraryPath(Opcodes.class, false) ); } catch (URISyntaxException | IllegalArgumentException e) { - e.printStackTrace(); - // fallback to "dumb" method - RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); - return runtimeMXBean.getClassPath() + p.getProperty("path.separator") + getLibraryPath(Utils.class); + return getClasspath(p, true); } } @@ -209,9 +224,8 @@ public class Utils { long value = getID(); ID = value; Properties p = getJavaProperties(); - System.out.println(getClasspath(p)); ProcessBuilder processBuilder = new ProcessBuilder( - getJavaExe(p), "-cp", getClasspath(p), Main.class.getName(), Long.toString(value) + getJavaExe(p), "-cp", getClasspath(p, false), Main.class.getName(), Long.toString(value) ); processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT).redirectError(ProcessBuilder.Redirect.INHERIT); Process c = processBuilder.start(); -- cgit From 0579088389caf554624787fcfae910a1d7bede71 Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Wed, 8 Feb 2023 09:25:37 -0800 Subject: More nightly trolling (it launches) --- .idea/artifacts/CopyMod.xml | 8 -- .idea/artifacts/CopyResources.xml | 8 -- SECURITY.md | 14 +-- .../nosession/NoSessionLoadingPlugin.java | 5 +- .../malwarefight/nosession/relaunch/Relaunch.java | 33 ++++--- .../nosession/tweaks/CleanupTweaker.java | 82 ---------------- .../nosession/tweaks/InitialTweaker.java | 104 --------------------- .../nosession/tweaks/cleanup/CleanupTweaker.java | 82 ++++++++++++++++ .../nosession/tweaks/cleanup/package-info.java | 5 + .../nosession/tweaks/initial/InitialTweaker.java | 97 +++++++++++++++++++ .../nosession/tweaks/initial/package-info.java | 5 + .../gq/malwarefight/nosession/utils/Utils.java | 77 ++++++--------- 12 files changed, 245 insertions(+), 275 deletions(-) delete mode 100644 .idea/artifacts/CopyMod.xml delete mode 100644 .idea/artifacts/CopyResources.xml delete mode 100644 src/main/java/gq/malwarefight/nosession/tweaks/CleanupTweaker.java delete mode 100644 src/main/java/gq/malwarefight/nosession/tweaks/InitialTweaker.java create mode 100644 src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java create mode 100644 src/main/java/gq/malwarefight/nosession/tweaks/cleanup/package-info.java create mode 100644 src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java create mode 100644 src/main/java/gq/malwarefight/nosession/tweaks/initial/package-info.java diff --git a/.idea/artifacts/CopyMod.xml b/.idea/artifacts/CopyMod.xml deleted file mode 100644 index 14c03c2..0000000 --- a/.idea/artifacts/CopyMod.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - $USER_HOME$/AppData/Roaming/.minecraft/mods - - - - - \ No newline at end of file diff --git a/.idea/artifacts/CopyResources.xml b/.idea/artifacts/CopyResources.xml deleted file mode 100644 index abf019e..0000000 --- a/.idea/artifacts/CopyResources.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - $PROJECT_DIR$/build/classes/main - - - - - \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index c64722e..7d9b630 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,21 +2,21 @@ ## Supported Versions -Currently all versions are supported. +Currently, all releaswed versions are supported. -| Version | Supported | -| ------- | ------------------ | -| 1.0.0 | ✔️ | -| Any nightly branch | ❌ | +| Version | Supported | +|--------------------|-----------| +| 1.0.0 | ✔️ | +| Any nightly branch | ❌ | ## Reporting a Vulnerability A vulnerability is currently defined as being able to get the session ID with only a mod that gets loaded after NoSession loads its tweaker.
Vulnerabilities that are out of scope are defined as those that NoSession itself cannot prevent. -However, if you can produce a patch for an out of scope vulnerability, a bug bounty will be awarded as well. +However, if you can produce a patch for an out-of-scope vulnerability, a bug bounty will be awarded as well. The bug bounty is a $5 USD Amazon Gift Card. I might run out, so it's awarded on a first come, first serve basis. -Report the bug bounty by emailing admin@malwarefight.gq or by sending a DM to PandaNinjas#3017 on Discord.
+Report the bug bounty by sending a DM to PandaNinjas#3017 on Discord.
If you would like, you can encrypt the message with my [public GPG key](https://raw.githubusercontent.com/pandaninjas/pandaninjas/main/pandaninjas-publickey.key)
Your bug bounty may be invalidated if you disclose it to the public before. diff --git a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java index 0ceff30..dd1c3e5 100644 --- a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java +++ b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java @@ -1,6 +1,6 @@ package gq.malwarefight.nosession; -import gq.malwarefight.nosession.tweaks.InitialTweaker; +import gq.malwarefight.nosession.tweaks.initial.InitialTweaker; import gq.malwarefight.nosession.utils.Utils; import net.minecraft.launchwrapper.Launch; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; @@ -11,7 +11,6 @@ import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; @@ -64,6 +63,7 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { } public static void lock() { + System.out.println("Waiting for lock"); while (true) { File f = new File("/home/pandaninjas/lock"); if (f.exists()) { @@ -74,7 +74,6 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { } static { - System.out.println("Waiting for lock"); lock(); addSelfToClassLoader(); try { diff --git a/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java b/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java index 55e907c..e712254 100644 --- a/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java +++ b/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java @@ -1,6 +1,6 @@ package gq.malwarefight.nosession.relaunch; -import gq.malwarefight.nosession.tweaks.CleanupTweaker; +import gq.malwarefight.nosession.tweaks.cleanup.CleanupTweaker; import gq.malwarefight.nosession.utils.Utils; import net.minecraft.launchwrapper.Launch; import net.minecraftforge.fml.client.FMLClientHandler; @@ -17,6 +17,8 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -32,17 +34,14 @@ public class Relaunch { setToNull(FMLClientHandler.class, "INSTANCE"); setToNull(Loader.class, "injectedContainers"); Utils.setStaticValue(FMLInjectionData.class, "containers", new ArrayList()); - resetClass(ASMTransformerWrapper.class); - URLClassLoader originalClassLoader = (URLClassLoader) Launch.class.getClassLoader(); - URL[] newURLS = new URL[originalClassLoader.getURLs().length + 1]; - URLClassLoader lcl = new URLClassLoader(newURLS, originalClassLoader); - //noinspection unchecked - Class innerLaunch = (Class) Class.forName("net.minecraft.launchwrapper.Launch", false, lcl); + resetTransformerWrapper(); + addSelfToClassloader(); + Class innerLaunch = Launch.class; Method launch = innerLaunch.getDeclaredMethod("main", String[].class); launch.invoke(null, (Object) constructArgs(args, gameDir, assetsDir, version)); } - public static String[] constructArgs(ArrayList initial, File gameDir, File assetDir, String version) { + private static String[] constructArgs(ArrayList initial, File gameDir, File assetDir, String version) { initial.add("--version"); initial.add(version); initial.add("--gameDir"); @@ -50,11 +49,11 @@ public class Relaunch { initial.add("--assetsDir"); initial.add(assetDir.getAbsolutePath()); initial.add("--tweakClass"); - initial.add("gq.malwarefight.nosession.tweaks.CleanupTweaker"); + initial.add(CleanupTweaker.class.getName()); return initial.toArray(new String[0]); } - public static void resetSecurityManager() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + private static void resetSecurityManager() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { Method m = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); m.setAccessible(true); Field[] fields = (Field[]) m.invoke(System.class, false); @@ -67,8 +66,8 @@ public class Relaunch { } - public static void resetClass(Class cls) throws IllegalAccessException, NoSuchFieldException { - Field[] fields = cls.getDeclaredFields(); + private static void resetTransformerWrapper() throws IllegalAccessException, NoSuchFieldException { + Field[] fields = ASMTransformerWrapper.class.getDeclaredFields(); for (Field field: fields) { if ((field.getModifiers() & Modifier.STATIC) != 0) { setToNull(field); @@ -76,12 +75,12 @@ public class Relaunch { } } - public static void setToNull(Class cls, String fieldname) throws NoSuchFieldException, IllegalAccessException { + private static void setToNull(Class cls, String fieldname) throws NoSuchFieldException, IllegalAccessException { Field f = cls.getDeclaredField(fieldname); setToNull(f); } - public static void setToNull(Field f) throws IllegalAccessException, NoSuchFieldException { + private static void setToNull(Field f) throws IllegalAccessException, NoSuchFieldException { f.setAccessible(true); if ((f.getModifiers() & Modifier.FINAL) != 0) { // if it is final Field modifiers = Field.class.getDeclaredField("modifiers"); @@ -97,4 +96,10 @@ public class Relaunch { } } + private static void addSelfToClassloader() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, URISyntaxException, MalformedURLException { + URLClassLoader ucl = (URLClassLoader) Launch.class.getClassLoader(); + Method addUrl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + addUrl.setAccessible(true); + addUrl.invoke(ucl, Utils.getLibraryPathAsFile(CleanupTweaker.class).toURI().toURL()); + } } diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/CleanupTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/CleanupTweaker.java deleted file mode 100644 index 6deeeb0..0000000 --- a/src/main/java/gq/malwarefight/nosession/tweaks/CleanupTweaker.java +++ /dev/null @@ -1,82 +0,0 @@ -package gq.malwarefight.nosession.tweaks; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.Launch; -import net.minecraft.launchwrapper.LaunchClassLoader; -import net.minecraftforge.fml.common.asm.ASMTransformerWrapper; - -import java.io.File; -import java.lang.reflect.*; -import java.net.URL; -import java.net.URLStreamHandler; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import gq.malwarefight.nosession.utils.Utils; - -public class CleanupTweaker implements ITweaker { - /** - * Cached to avoid expensive reflection operations - */ - public Method makeWrapper; - - public CleanupTweaker() throws NoSuchMethodException { - makeWrapper = ASMTransformerWrapper.class.getDeclaredMethod("makeWrapper", String.class); - makeWrapper.setAccessible(true); - } - @Override - @SuppressWarnings("unchecked") - public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - // add the FMLTweaker - ArrayList tweakerList = (ArrayList) Launch.blackboard.get("TweakClasses"); - tweakerList.add("net.minecraftforge.fml.common.launcher.FMLTweaker"); - // reset ASMTransformerWrapper - Class cls = ASMTransformerWrapper.class; - try { - Utils.setStaticValue(cls, "wrapperModMap", new HashMap()); - Utils.setStaticValue(cls, "wrapperParentMap", new HashMap()); - Utils.setStaticValue(cls, "wrapperCache", CacheBuilder.newBuilder().maximumSize(30L).weakValues().build(new CacheLoader() { - public byte[] load(String file) throws Exception { - return (byte[]) makeWrapper.invoke(null, file); - } - })); - Utils.setStaticValue(cls, "asmGenRoot", new URL("asmgen", null, -1, "/", getAsmGenHandler())); - Utils.setStaticValue(cls, "injected", false); - } catch (Exception e) { - System.err.println("NoSession: Fixing ASMTransformerWrapper failed. Things may break."); - e.printStackTrace(); - } - } - - @Override - public void injectIntoClassLoader(LaunchClassLoader classLoader) {} - - @Override - public String getLaunchTarget() { - return "net.minecraft.client.main.Main"; - } - - @Override - public String[] getLaunchArguments() { - return new String[0]; - } - - - - @SuppressWarnings("unchecked") - private URLStreamHandler getAsmGenHandler() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - Class cls = ASMTransformerWrapper.class; - for (Class candidate: cls.getDeclaredClasses()) { - if (candidate.getSimpleName().equals("ASMGenHandler")) { - Method pgdc = Class.class.getDeclaredMethod("privateGetDeclaredConstructors", boolean.class); - pgdc.setAccessible(true); - Constructor constructor = ((Constructor[]) pgdc.invoke(candidate, false))[0]; - constructor.setAccessible(true); - return constructor.newInstance(); - } - } - throw new RuntimeException("NoSession: unable to get the ASMGenHandler"); - } -} diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/InitialTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/InitialTweaker.java deleted file mode 100644 index 74d05f5..0000000 --- a/src/main/java/gq/malwarefight/nosession/tweaks/InitialTweaker.java +++ /dev/null @@ -1,104 +0,0 @@ -package gq.malwarefight.nosession.tweaks; - -import gq.malwarefight.nosession.relaunch.Relaunch; -import gq.malwarefight.nosession.utils.Utils; -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.Launch; -import net.minecraft.launchwrapper.LaunchClassLoader; -import org.spongepowered.asm.launch.MixinBootstrap; -import org.spongepowered.asm.mixin.MixinEnvironment; -import org.spongepowered.asm.mixin.Mixins; - -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.Socket; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class InitialTweaker implements ITweaker { - - public InitialTweaker() { - System.gc(); // try to garbage collect the earlier launch data - } - - /** - * This handles the launch arguments passed towards minecraft - * @param args The launch arguments - * @param gameDir The game directory (ie: .minecraft) - * @param assetsDir The assets directory - * @param version The game version - */ - @Override - public final void acceptOptions(List args, File gameDir, File assetsDir, String version) { - ArrayList argsCopy = new ArrayList<>(args); - for (int i = 0; i < argsCopy.size(); i++) { - if (argsCopy.get(i).equals("--accessToken")) { - if (argsCopy.get(i + 1).equals("")) { - Runtime.getRuntime().addShutdownHook( - new Thread(() -> { - try { - Socket socket = Utils.getProperSocket(); - socket.getOutputStream().write("fullquit\n".getBytes(StandardCharsets.UTF_8)); - socket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - }) - ); - return; // don't do anything, the change has already been made - } - try { - Utils.setToken(args.get(i + 1)); - } catch (Exception e) { - throw new RuntimeException(e); - } - argsCopy.set(i + 1, ""); - } - } - System.out.println("======================="); - System.out.println("NoSession: relaunching without the token"); - System.out.println("======================="); - try { - Relaunch.relaunch(argsCopy, gameDir, assetsDir, version); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - - } - - /** - * Inject into the MC class loader - * @param classLoader The class loader - */ - @Override - public final void injectIntoClassLoader(LaunchClassLoader classLoader) { - MixinBootstrap.init(); - MixinEnvironment environment = MixinEnvironment.getDefaultEnvironment(); - Mixins.addConfiguration("mixins.nosession.json"); - // Check if the obfuscation context is null - if (environment.getObfuscationContext() == null) { - environment.setObfuscationContext("notch"); - } - // This is a client side, client :) - environment.setSide(MixinEnvironment.Side.CLIENT); - } - - @Override - public String getLaunchTarget() { - return MixinBootstrap.getPlatform().getLaunchTarget(); - } - - @Override - public String[] getLaunchArguments() { - return new String[0]; - } - -} diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java new file mode 100644 index 0000000..c0e2df4 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java @@ -0,0 +1,82 @@ +package gq.malwarefight.nosession.tweaks.cleanup; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; +import net.minecraftforge.fml.common.asm.ASMTransformerWrapper; + +import java.io.File; +import java.lang.reflect.*; +import java.net.URL; +import java.net.URLStreamHandler; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import gq.malwarefight.nosession.utils.Utils; + +public class CleanupTweaker implements ITweaker { + /** + * Cached to avoid expensive reflection operations + */ + public Method makeWrapper; + + public CleanupTweaker() throws NoSuchMethodException { + makeWrapper = ASMTransformerWrapper.class.getDeclaredMethod("makeWrapper", String.class); + makeWrapper.setAccessible(true); + } + @Override + @SuppressWarnings("unchecked") + public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { + // add the FMLTweaker + ArrayList tweakerList = (ArrayList) Launch.blackboard.get("TweakClasses"); + tweakerList.add("net.minecraftforge.fml.common.launcher.FMLTweaker"); + // reset ASMTransformerWrapper + Class cls = ASMTransformerWrapper.class; + try { + Utils.setStaticValue(cls, "wrapperModMap", new HashMap()); + Utils.setStaticValue(cls, "wrapperParentMap", new HashMap()); + Utils.setStaticValue(cls, "wrapperCache", CacheBuilder.newBuilder().maximumSize(30L).weakValues().build(new CacheLoader() { + public byte[] load(String file) throws Exception { + return (byte[]) makeWrapper.invoke(null, file); + } + })); + Utils.setStaticValue(cls, "asmGenRoot", new URL("asmgen", null, -1, "/", getAsmGenHandler())); + Utils.setStaticValue(cls, "injected", false); + } catch (Exception e) { + System.err.println("NoSession: Fixing ASMTransformerWrapper failed. Things may break."); + e.printStackTrace(); + } + } + + @Override + public void injectIntoClassLoader(LaunchClassLoader classLoader) {} + + @Override + public String getLaunchTarget() { + return "net.minecraft.client.main.Main"; + } + + @Override + public String[] getLaunchArguments() { + return new String[0]; + } + + + + @SuppressWarnings("unchecked") + private URLStreamHandler getAsmGenHandler() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + Class cls = ASMTransformerWrapper.class; + for (Class candidate: cls.getDeclaredClasses()) { + if (candidate.getSimpleName().equals("ASMGenHandler")) { + Method pgdc = Class.class.getDeclaredMethod("privateGetDeclaredConstructors", boolean.class); + pgdc.setAccessible(true); + Constructor constructor = ((Constructor[]) pgdc.invoke(candidate, false))[0]; + constructor.setAccessible(true); + return constructor.newInstance(); + } + } + throw new RuntimeException("NoSession: unable to get the ASMGenHandler"); + } +} diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/package-info.java b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/package-info.java new file mode 100644 index 0000000..c2a48ea --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/package-info.java @@ -0,0 +1,5 @@ +/** + * Due to the launchwrapper using a launchclassloader exclusion on every tweaker's package, we separate the two tweakers + * so that the loader exclusion doesn't affect anything else + */ +package gq.malwarefight.nosession.tweaks.cleanup; \ No newline at end of file diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java new file mode 100644 index 0000000..e2ca1ae --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java @@ -0,0 +1,97 @@ +package gq.malwarefight.nosession.tweaks.initial; + +import gq.malwarefight.nosession.relaunch.Relaunch; +import gq.malwarefight.nosession.utils.Utils; +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.LaunchClassLoader; +import org.spongepowered.asm.launch.MixinBootstrap; +import org.spongepowered.asm.mixin.MixinEnvironment; +import org.spongepowered.asm.mixin.Mixins; + +import java.io.File; +import java.io.IOException; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class InitialTweaker implements ITweaker { + + public InitialTweaker() { + System.gc(); // try to garbage collect the earlier launch data + } + + /** + * This handles the launch arguments passed towards minecraft + * @param args The launch arguments + * @param gameDir The game directory (ie: .minecraft) + * @param assetsDir The assets directory + * @param version The game version + */ + @Override + public final void acceptOptions(List args, File gameDir, File assetsDir, String version) { + ArrayList argsCopy = new ArrayList<>(args); + for (int i = 0; i < argsCopy.size(); i++) { + if (argsCopy.get(i).equals("--accessToken")) { + if (argsCopy.get(i + 1).equals("")) { + Runtime.getRuntime().addShutdownHook( + new Thread(() -> { + try { + Socket socket = Utils.getProperSocket(); + socket.getOutputStream().write("fullquit\n".getBytes(StandardCharsets.UTF_8)); + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + }) + ); + return; // don't do anything, the change has already been made + } + try { + Utils.setToken(args.get(i + 1)); + } catch (Exception e) { + throw new RuntimeException(e); + } + argsCopy.set(i + 1, ""); + } + } + System.out.println("======================="); + System.out.println("NoSession: relaunching without the token"); + System.out.println("======================="); + try { + Relaunch.relaunch(argsCopy, gameDir, assetsDir, version); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + + } + + /** + * Inject into the MC class loader + * @param classLoader The class loader + */ + @Override + public final void injectIntoClassLoader(LaunchClassLoader classLoader) { + MixinBootstrap.init(); + MixinEnvironment environment = MixinEnvironment.getDefaultEnvironment(); + Mixins.addConfiguration("mixins.nosession.json"); + // Check if the obfuscation context is null + if (environment.getObfuscationContext() == null) { + environment.setObfuscationContext("notch"); + } + // This is a client side, client :) + environment.setSide(MixinEnvironment.Side.CLIENT); + } + + @Override + public String getLaunchTarget() { + return MixinBootstrap.getPlatform().getLaunchTarget(); + } + + @Override + public String[] getLaunchArguments() { + return new String[0]; + } + +} diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/initial/package-info.java b/src/main/java/gq/malwarefight/nosession/tweaks/initial/package-info.java new file mode 100644 index 0000000..443e379 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/tweaks/initial/package-info.java @@ -0,0 +1,5 @@ +/** + * Due to the launchwrapper using a launchclassloader exclusion on every tweaker's package, we separate the two tweakers + * so that the loader exclusion doesn't affect anything else + */ +package gq.malwarefight.nosession.tweaks.initial; \ No newline at end of file diff --git a/src/main/java/gq/malwarefight/nosession/utils/Utils.java b/src/main/java/gq/malwarefight/nosession/utils/Utils.java index ae0e273..139f1e8 100644 --- a/src/main/java/gq/malwarefight/nosession/utils/Utils.java +++ b/src/main/java/gq/malwarefight/nosession/utils/Utils.java @@ -4,7 +4,6 @@ import com.google.common.annotations.Beta; import com.google.common.collect.ForwardingMultimap; import com.google.gson.Gson; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; -import gq.malwarefight.nosession.tweaks.InitialTweaker; import gq.malwarefight.tokenapp.Main; import org.apache.commons.io.ByteOrderMark; import org.apache.commons.lang3.CharEncoding; @@ -17,16 +16,11 @@ import org.objectweb.asm.Opcodes; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.URISyntaxException; +import java.net.*; import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; @@ -118,50 +112,33 @@ public class Utils { } } - public static String getLibraryPath(Class c, boolean processString) throws URISyntaxException { - String uri = c.getProtectionDomain().getCodeSource().getLocation().toURI().toString(); - if (processString) { - uri = processString(uri); + public static File getLibraryPathAsFile(Class c) throws URISyntaxException { + String uri = c.getProtectionDomain().getCodeSource().getLocation().toURI().toString().replace("%20", " "); // code breakage in 3, 2, 1... + if (uri.endsWith(".class")) { + uri = processString(uri); // stupid reference to a class within a jar } - return Paths.get(uri).toString(); + return new File(uri); } - private static String getClasspath(Properties p, boolean processString) throws URISyntaxException { - if (processString) { - return String.join( - p.getProperty("path.separator"), - getLibraryPath(Utils.class, true), - getLibraryPath(YggdrasilAuthenticationService.class, true), - getLibraryPath(Gson.class, true), - getLibraryPath(LogManager.class, true), - getLibraryPath(Validate.class, true), - getLibraryPath(ForwardingMultimap.class, true), - getLibraryPath(Beta.class, true), - getLibraryPath(CharEncoding.class, true), - getLibraryPath(ByteOrderMark.class, true), - getLibraryPath(Logger.class, true), - getLibraryPath(Opcodes.class, true) - ); - } - try { - // try to be smart - return String.join( - p.getProperty("path.separator"), - getLibraryPath(Utils.class, false), - getLibraryPath(YggdrasilAuthenticationService.class, false), - getLibraryPath(Gson.class, false), - getLibraryPath(LogManager.class, false), - getLibraryPath(Validate.class, false), - getLibraryPath(ForwardingMultimap.class, false), - getLibraryPath(Beta.class, false), - getLibraryPath(CharEncoding.class, false), - getLibraryPath(ByteOrderMark.class, false), - getLibraryPath(Logger.class, false), - getLibraryPath(Opcodes.class, false) - ); - } catch (URISyntaxException | IllegalArgumentException e) { - return getClasspath(p, true); - } + public static String getLibraryPath(Class c) throws URISyntaxException { + return getLibraryPathAsFile(c).getAbsolutePath(); + } + + private static String getClasspath(Properties p) throws URISyntaxException { + return String.join( + p.getProperty("path.separator"), + getLibraryPath(Main.class), + getLibraryPath(YggdrasilAuthenticationService.class), + getLibraryPath(Gson.class), + getLibraryPath(LogManager.class), + getLibraryPath(Validate.class), + getLibraryPath(ForwardingMultimap.class), + getLibraryPath(Beta.class), + getLibraryPath(CharEncoding.class), + getLibraryPath(ByteOrderMark.class), + getLibraryPath(Logger.class), + getLibraryPath(Opcodes.class) + ); } public static boolean createLockFile(long value) { @@ -224,8 +201,10 @@ public class Utils { long value = getID(); ID = value; Properties p = getJavaProperties(); + String cp = getClasspath(p); + System.out.println(cp); ProcessBuilder processBuilder = new ProcessBuilder( - getJavaExe(p), "-cp", getClasspath(p, false), Main.class.getName(), Long.toString(value) + getJavaExe(p), "-cp", getClasspath(p), Main.class.getName(), Long.toString(value) ); processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT).redirectError(ProcessBuilder.Redirect.INHERIT); Process c = processBuilder.start(); -- cgit From 661d40251d6f8f98497aa61f5ea2e3c67f8e9190 Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Thu, 9 Feb 2023 19:17:16 -0800 Subject: it works:tm: --- README.md | 4 +- SECURITY.md | 2 +- build.gradle | 22 ++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- .../nosession/NoSessionLoadingPlugin.java | 13 ----- .../mixin/client/YggdrasilSessionMixin.java | 3 +- .../nosession/tweaks/cleanup/CleanupTweaker.java | 1 + .../nosession/tweaks/initial/InitialTweaker.java | 37 ++++++++----- .../gq/malwarefight/nosession/utils/Utils.java | 62 ++++++---------------- src/main/java/gq/malwarefight/tokenapp/Main.java | 3 +- .../gq/malwarefight/tokenapp/SocketThread.java | 4 +- src/main/resources/mixins.nosession.json | 2 +- 12 files changed, 58 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 925d2a6..8345802 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ This mod doesn't make you 100% safe, but it makes it much harder to steal your s [staying safe section](#staying-safe) ## Staying Safe -In order to work around an unpatchable security vulnerability, rename the NoSession jar to !.jar so it can load its protection before any other mods.
+In order to work around an unpatchable security vulnerability, rename the NoSession jar to !.jar, so it can load its protection before any other mods.
You should also use [MultiMC](https://github.com/MultiMC/Launcher/) or one of its derivates [PolyMC](https://github.com/PolyMC/PolyMC) or [PrismLauncher](https://github.com/PrismLauncher/PrismLauncher), because they use launch Minecraft in a way that improves security.
This only protects you from other mods. There are fake verification sites that can steal your session ID through that method.
-Don't login with Microsoft OAuth to anything except maybe your Minecraft launcher. You may also want to verify the signature on any NoSession binary. It's signed with [pandaninjas' GPG key](https://raw.githubusercontent.com/pandaninjas/pandaninjas/main/pandaninjas-publickey.key). +Don't log in with Microsoft OAuth to anything except maybe your Minecraft launcher. You may also want to verify the signature on any NoSession binary. It's signed with [pandaninjas' GPG key](https://raw.githubusercontent.com/pandaninjas/pandaninjas/main/pandaninjas-publickey.key). See [ILikePlayingGames' SkyblockModSafety guide](https://github.com/ILikePlayingGames/SkyblockModSafety) for other information diff --git a/SECURITY.md b/SECURITY.md index 7d9b630..4a30fd7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -15,7 +15,7 @@ Vulnerabilities that are out of scope are defined as those that NoSession itself However, if you can produce a patch for an out-of-scope vulnerability, a bug bounty will be awarded as well. -The bug bounty is a $5 USD Amazon Gift Card. I might run out, so it's awarded on a first come, first serve basis. +The bug bounty is a $5 USD Amazon Gift Card. I might run out, so it's awarded on a first come, first served basis. Report the bug bounty by sending a DM to PandaNinjas#3017 on Discord.
If you would like, you can encrypt the message with my [public GPG key](https://raw.githubusercontent.com/pandaninjas/pandaninjas/main/pandaninjas-publickey.key)
diff --git a/build.gradle b/build.gradle index d7a1031..c54ce08 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,6 @@ buildscript { plugins { id "java" - id "com.github.johnrengelman.shadow" version "2.0.4" } apply plugin: 'net.minecraftforge.gradle.forge' @@ -45,7 +44,7 @@ repositories { } dependencies { - shade('org.spongepowered:mixin:0.7.10-SNAPSHOT') { + shade('org.spongepowered:mixin:0.7.11-SNAPSHOT') { exclude module: 'launchwrapper' exclude module: 'guava' exclude module: 'gson' @@ -73,19 +72,16 @@ processResources { rename '(.+_at.cfg)', 'META-INF/$1' } -shadowJar { - dependencies {} - configurations = [project.configurations.shade] - duplicatesStrategy DuplicatesStrategy.EXCLUDE //prevent duplicates - classifier "" //prevent creation of unshadowed jar -} - -reobf { - //reobfuscate the shadowed jar - shadowJar {} -} jar { + dependsOn configurations.compile + from { + configurations.compile.collect { + it.isDirectory() ? it : zipTree(it) + } + } + exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA', 'dummyThing' + manifest { attributes "ForceLoadAsMod": true, "TweakOrder": 0, diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 357df5f..ce94abc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip \ No newline at end of file diff --git a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java index dd1c3e5..a7be261 100644 --- a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java +++ b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java @@ -5,7 +5,6 @@ import gq.malwarefight.nosession.utils.Utils; import net.minecraft.launchwrapper.Launch; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; -import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; @@ -62,19 +61,7 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { Launch.classLoader.addURL(NoSessionLoadingPlugin.class.getProtectionDomain().getCodeSource().getLocation()); } - public static void lock() { - System.out.println("Waiting for lock"); - while (true) { - File f = new File("/home/pandaninjas/lock"); - if (f.exists()) { - f.delete(); - break; - } - } - } - static { - lock(); addSelfToClassLoader(); try { Properties p = Utils.getJavaProperties(); 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 5451c26..25636f7 100644 --- a/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java +++ b/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java @@ -1,6 +1,5 @@ package gq.malwarefight.nosession.mixin.client; -import com.google.common.primitives.Booleans; import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; @@ -21,7 +20,7 @@ public class YggdrasilSessionMixin { @Inject(method = "joinServer", at = @At("HEAD"), cancellable = true, remap = false) public void joinServer(GameProfile profile, String authenticationToken, String serverId, CallbackInfo ci) throws AuthenticationException { if (authenticationToken.equals("")) { // this token has been messed with by the Tweaker - Socket socket = Utils.getProperSocket(); + Socket socket = Utils.getProperSocket(profile.getId()); try { assert socket != null; socket.getOutputStream().write(("login " + serverId + "\n").getBytes(StandardCharsets.UTF_8)); diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java index c0e2df4..a0d458b 100644 --- a/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java +++ b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java @@ -37,6 +37,7 @@ public class CleanupTweaker implements ITweaker { try { Utils.setStaticValue(cls, "wrapperModMap", new HashMap()); Utils.setStaticValue(cls, "wrapperParentMap", new HashMap()); + //noinspection UnstableApiUsage Utils.setStaticValue(cls, "wrapperCache", CacheBuilder.newBuilder().maximumSize(30L).weakValues().build(new CacheLoader() { public byte[] load(String file) throws Exception { return (byte[]) makeWrapper.invoke(null, file); 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 e2ca1ae..21b2646 100644 --- a/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java +++ b/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java @@ -9,11 +9,11 @@ import org.spongepowered.asm.mixin.MixinEnvironment; import org.spongepowered.asm.mixin.Mixins; import java.io.File; -import java.io.IOException; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.UUID; public class InitialTweaker implements ITweaker { @@ -30,22 +30,13 @@ public class InitialTweaker implements ITweaker { */ @Override public final void acceptOptions(List args, File gameDir, File assetsDir, String version) { + boolean isRelaunch = false; // is this invocation from a relaunch? + String uuid = null; ArrayList argsCopy = new ArrayList<>(args); for (int i = 0; i < argsCopy.size(); i++) { if (argsCopy.get(i).equals("--accessToken")) { if (argsCopy.get(i + 1).equals("")) { - Runtime.getRuntime().addShutdownHook( - new Thread(() -> { - try { - Socket socket = Utils.getProperSocket(); - socket.getOutputStream().write("fullquit\n".getBytes(StandardCharsets.UTF_8)); - socket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - }) - ); - return; // don't do anything, the change has already been made + isRelaunch = true; } try { Utils.setToken(args.get(i + 1)); @@ -53,8 +44,28 @@ public class InitialTweaker implements ITweaker { throw new RuntimeException(e); } argsCopy.set(i + 1, ""); + } else if (argsCopy.get(i).equals("--uuid")) { + uuid = argsCopy.get(i + 1); } } + if (isRelaunch) { + String finalUuid = uuid; + if (finalUuid == null) { + return; // if we don't have a uuid, quit + } + Runtime.getRuntime().addShutdownHook( + new Thread(() -> { + try { + Socket socket = Utils.getProperSocket(UUID.fromString(finalUuid)); + socket.getOutputStream().write("fullquit\n".getBytes(StandardCharsets.UTF_8)); + socket.close(); + } catch (Exception e) { + e.printStackTrace(); + } + }) + ); + return; // don't do anything, the change has already been made + } System.out.println("======================="); System.out.println("NoSession: relaunching without the token"); System.out.println("======================="); diff --git a/src/main/java/gq/malwarefight/nosession/utils/Utils.java b/src/main/java/gq/malwarefight/nosession/utils/Utils.java index 139f1e8..7d3c4dd 100644 --- a/src/main/java/gq/malwarefight/nosession/utils/Utils.java +++ b/src/main/java/gq/malwarefight/nosession/utils/Utils.java @@ -20,19 +20,18 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.*; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Properties; -import java.util.Random; +import java.util.UUID; public class Utils { public static int PORT = -1; - public static long ID = -1; private static final int BASE_PORT = 47777; public static byte[] read(InputStream i, Character delimiter) throws IOException { @@ -60,23 +59,25 @@ public class Utils { return new String(read(i, delimiter), StandardCharsets.UTF_8); } - public static Socket getProperSocket() { + public static Socket getProperSocket(UUID id) { if (PORT == -1) { Socket socket = null; - int port = 0; + int port; for (int i = BASE_PORT; i < BASE_PORT + 10; i++) { try { socket = new Socket(); socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), i)); - socket.getOutputStream().write("id\n".getBytes(StandardCharsets.UTF_8)); + socket.getOutputStream().write("uuid\n".getBytes(StandardCharsets.UTF_8)); String value = readString(socket.getInputStream(), '\n'); - if (value.equals(Long.toString(ID))) { + if (UUID.fromString(value).equals(id)) { port = i; + PORT = port; break; } - } catch (Exception ignored) {} + } catch (Exception exception) { + socket = null; + } } - PORT = port; return socket; } else { try { @@ -85,7 +86,7 @@ public class Utils { return socket; } catch (IOException e) { PORT = -1; - return getProperSocket(); + return getProperSocket(id); } } } @@ -141,37 +142,6 @@ public class Utils { ); } - public static boolean createLockFile(long value) { - Path path = Paths.get(System.getProperty("java.io.tmpdir"), "NoSessionLock" + value); - try { - Files.createFile(path); - } catch (FileAlreadyExistsException e) { - LogManager.getLogger().info("You won the lottery! Two NoSession instances used the same ID. The chance that a new NoSession instance uses the same ID as an old one is (NoSession instances) / 2^64"); - return false; - } catch (IOException e) { - LogManager.getLogger().warn("Failed to create lockfile " + e.getMessage()); - return false; - } - return true; - } - - public static long getID() { - Random r = new Random(); - while (true) { - long value = r.nextLong(); - if (createLockFile(value)) { - Runtime.getRuntime().addShutdownHook( - new Thread(() -> { - try { - Files.delete(Paths.get(System.getProperty("java.io.tmpdir"), "NoSessionLock" + value)); - } catch (Exception ignored) {} - }) - ); - return value; - } - } - } - public static Properties getJavaProperties() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Properties p = new Properties(); Method m = System.class.getDeclaredMethod("initProperties", Properties.class); @@ -198,13 +168,11 @@ public class Utils { } public static void setToken(String token) throws IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, URISyntaxException { - long value = getID(); - ID = value; Properties p = getJavaProperties(); String cp = getClasspath(p); System.out.println(cp); ProcessBuilder processBuilder = new ProcessBuilder( - getJavaExe(p), "-cp", getClasspath(p), Main.class.getName(), Long.toString(value) + getJavaExe(p), "-cp", getClasspath(p), Main.class.getName() ); processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT).redirectError(ProcessBuilder.Redirect.INHERIT); Process c = processBuilder.start(); diff --git a/src/main/java/gq/malwarefight/tokenapp/Main.java b/src/main/java/gq/malwarefight/tokenapp/Main.java index 62a4da3..ecea1f3 100644 --- a/src/main/java/gq/malwarefight/tokenapp/Main.java +++ b/src/main/java/gq/malwarefight/tokenapp/Main.java @@ -15,13 +15,13 @@ import java.util.UUID; public class Main { public static final int BASE_PORT = 47777; - public static long ID; public static YggdrasilMinecraftSessionService sessionService = null; public static YggdrasilAuthenticationService authenticationService = null; public static GameProfile gameProfile = null; public static void setup() throws IOException { String token = Utils.readString(System.in, '\n'); + System.out.println(token); YggdrasilAuthenticationService yas = new YggdrasilAuthenticationService(Proxy.NO_PROXY, token); authenticationService = yas; sessionService = (YggdrasilMinecraftSessionService) yas.createMinecraftSessionService(); @@ -40,7 +40,6 @@ public class Main { } public static void main(String[] args) { - ID = Long.parseLong(args[0]); try { setup(); } catch (Exception e) { diff --git a/src/main/java/gq/malwarefight/tokenapp/SocketThread.java b/src/main/java/gq/malwarefight/tokenapp/SocketThread.java index de029c1..f2d0be7 100644 --- a/src/main/java/gq/malwarefight/tokenapp/SocketThread.java +++ b/src/main/java/gq/malwarefight/tokenapp/SocketThread.java @@ -50,8 +50,8 @@ public class SocketThread extends Thread { sock.close(); } else if ("fullquit".equals(parts[0])) { System.exit(0); - } else if ("id".equals(parts[0])) { - writeResponse(Long.toString(Main.ID), out); + } else if ("uuid".equals(parts[0])) { + writeResponse(Main.gameProfile.getId().toString(), out); } else { writeResponse("418 I'm a teapot", out); } diff --git a/src/main/resources/mixins.nosession.json b/src/main/resources/mixins.nosession.json index 9570025..9483734 100644 --- a/src/main/resources/mixins.nosession.json +++ b/src/main/resources/mixins.nosession.json @@ -6,5 +6,5 @@ "client": [ "client.YggdrasilSessionMixin" ], - "minVersion": "0.7.10" + "minVersion": "0.7.11" } \ No newline at end of file -- cgit From ca54d9316d01040823815462f65f810c91143c04 Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Thu, 9 Feb 2023 19:33:00 -0800 Subject: Google code style but with 4 spaces instead of 2 --- .../nosession/tweaks/initial/InitialTweaker.java | 6 +++--- src/main/java/gq/malwarefight/nosession/utils/Utils.java | 8 ++++++-- src/main/java/gq/malwarefight/tokenapp/Main.java | 15 ++++++++------- 3 files changed, 17 insertions(+), 12 deletions(-) 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 21b2646..aae8bb6 100644 --- a/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java +++ b/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java @@ -49,14 +49,14 @@ public class InitialTweaker implements ITweaker { } } if (isRelaunch) { - String finalUuid = uuid; - if (finalUuid == null) { + String finalUUID = uuid; + if (finalUUID == null) { return; // if we don't have a uuid, quit } Runtime.getRuntime().addShutdownHook( new Thread(() -> { try { - Socket socket = Utils.getProperSocket(UUID.fromString(finalUuid)); + Socket socket = Utils.getProperSocket(UUID.fromString(Utils.normalizeUUID(finalUUID))); 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 7d3c4dd..b34c0aa 100644 --- a/src/main/java/gq/malwarefight/nosession/utils/Utils.java +++ b/src/main/java/gq/malwarefight/nosession/utils/Utils.java @@ -91,6 +91,12 @@ public class Utils { } } + public static String normalizeUUID(String uuid) { + return uuid.replaceFirst( + "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5" + ); + } + public static void setStaticValue(Class cls, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { Field f = cls.getDeclaredField(fieldName); f.setAccessible(true); @@ -169,8 +175,6 @@ public class Utils { public static void setToken(String token) throws IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, URISyntaxException { Properties p = getJavaProperties(); - String cp = getClasspath(p); - System.out.println(cp); ProcessBuilder processBuilder = new ProcessBuilder( getJavaExe(p), "-cp", getClasspath(p), Main.class.getName() ); diff --git a/src/main/java/gq/malwarefight/tokenapp/Main.java b/src/main/java/gq/malwarefight/tokenapp/Main.java index ecea1f3..96b9cb3 100644 --- a/src/main/java/gq/malwarefight/tokenapp/Main.java +++ b/src/main/java/gq/malwarefight/tokenapp/Main.java @@ -14,6 +14,7 @@ import java.net.*; import java.util.UUID; public class Main { + public static final int BASE_PORT = 47777; public static YggdrasilMinecraftSessionService sessionService = null; public static YggdrasilAuthenticationService authenticationService = null; @@ -21,18 +22,17 @@ public class Main { public static void setup() throws IOException { String token = Utils.readString(System.in, '\n'); - System.out.println(token); - YggdrasilAuthenticationService yas = new YggdrasilAuthenticationService(Proxy.NO_PROXY, token); + YggdrasilAuthenticationService yas = new YggdrasilAuthenticationService(Proxy.NO_PROXY, + token); authenticationService = yas; sessionService = (YggdrasilMinecraftSessionService) yas.createMinecraftSessionService(); - HttpsURLConnection httpsURLConnection = (HttpsURLConnection) (new URL("https://api.minecraftservices.com/minecraft/profile").openConnection()); + HttpsURLConnection httpsURLConnection = (HttpsURLConnection) (new URL( + "https://api.minecraftservices.com/minecraft/profile").openConnection()); httpsURLConnection.setRequestProperty("Authorization", "Bearer " + token); String response = Utils.readString(httpsURLConnection.getInputStream(), null); JsonObject jsonObject = new JsonParser().parse(response).getAsJsonObject(); UUID id = UUID.fromString( - jsonObject.get("id").getAsString().replaceFirst( - "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5" - ) + Utils.normalizeUUID(jsonObject.get("id").getAsString()) ); String name = jsonObject.get("name").getAsString(); gameProfile = new GameProfile(id, name); @@ -52,7 +52,8 @@ public class Main { //noinspection resource sock = new ServerSocket(i, 50, InetAddress.getLoopbackAddress()); break; - } catch (Exception ignored) {} + } catch (Exception ignored) { + } } if (sock == null) { System.err.println("Could not bind to any valid port"); -- cgit From 32352ad6726394c0f8fd9a5df9bd0f75d4490fa8 Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Thu, 9 Feb 2023 20:04:19 -0800 Subject: Code things idk --- .../malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java | 6 +++++- src/main/java/gq/malwarefight/tokenapp/Main.java | 3 ++- src/main/java/gq/malwarefight/tokenapp/SocketThread.java | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) 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 25636f7..ff07cab 100644 --- a/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java +++ b/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java @@ -43,7 +43,11 @@ public class YggdrasilSessionMixin { try { socket.getOutputStream().write("disconnect\n".getBytes(StandardCharsets.UTF_8)); socket.close(); - } catch (IOException ignored) {} + } catch (IOException ignored) { + // it doesn't matter if the socket is disconnected + // if there's an IOException, the socket is either closed or + // impossible to close/unusable + } } } } \ 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 96b9cb3..f86711b 100644 --- a/src/main/java/gq/malwarefight/tokenapp/Main.java +++ b/src/main/java/gq/malwarefight/tokenapp/Main.java @@ -53,6 +53,7 @@ public class Main { sock = new ServerSocket(i, 50, InetAddress.getLoopbackAddress()); break; } catch (Exception ignored) { + // we couldn't bind to the port, try the next one } } if (sock == null) { @@ -64,7 +65,7 @@ public class Main { Socket connection = sock.accept(); Thread t = new SocketThread(connection); t.start(); - } catch (IOException ignored) { + } catch (IOException exception) { System.exit(0); } } diff --git a/src/main/java/gq/malwarefight/tokenapp/SocketThread.java b/src/main/java/gq/malwarefight/tokenapp/SocketThread.java index f2d0be7..e0f69dc 100644 --- a/src/main/java/gq/malwarefight/tokenapp/SocketThread.java +++ b/src/main/java/gq/malwarefight/tokenapp/SocketThread.java @@ -57,6 +57,8 @@ public class SocketThread extends Thread { } } - } catch (IOException ignored) {} + } catch (IOException ignored) { + // if something goes wrong, don't worry! just quit immediately + } } } -- cgit From 61e48d030b25c11e9636bec750a189bad1b35929 Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Fri, 10 Feb 2023 18:21:17 +0000 Subject: Update SECURITY.md --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 4a30fd7..7d9b630 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -15,7 +15,7 @@ Vulnerabilities that are out of scope are defined as those that NoSession itself However, if you can produce a patch for an out-of-scope vulnerability, a bug bounty will be awarded as well. -The bug bounty is a $5 USD Amazon Gift Card. I might run out, so it's awarded on a first come, first served basis. +The bug bounty is a $5 USD Amazon Gift Card. I might run out, so it's awarded on a first come, first serve basis. Report the bug bounty by sending a DM to PandaNinjas#3017 on Discord.
If you would like, you can encrypt the message with my [public GPG key](https://raw.githubusercontent.com/pandaninjas/pandaninjas/main/pandaninjas-publickey.key)
-- cgit From 55bd37f26b8d9f99b2296636048bffc63880f95d Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Fri, 10 Feb 2023 18:25:52 +0000 Subject: Update SECURITY.md --- SECURITY.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 7d9b630..54de5da 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,11 +2,12 @@ ## Supported Versions -Currently, all releaswed versions are supported. +Generally, the latest release will be supported. Nightly branches are never supported unless the author supports it | Version | Supported | |--------------------|-----------| -| 1.0.0 | ✔️ | +| 1.0.0 | ❌ | +| 1.1.0 | ✔️ | | Any nightly branch | ❌ | ## Reporting a Vulnerability @@ -19,4 +20,4 @@ The bug bounty is a $5 USD Amazon Gift Card. I might run out, so it's awarded on Report the bug bounty by sending a DM to PandaNinjas#3017 on Discord.
If you would like, you can encrypt the message with my [public GPG key](https://raw.githubusercontent.com/pandaninjas/pandaninjas/main/pandaninjas-publickey.key)
-Your bug bounty may be invalidated if you disclose it to the public before. +Your bug bounty may be invalidated if you disclose it to the public before. You must be the first to report a vulnerability -- cgit From ffed53e2c70cba8b6abf01e6cdc987721fc66e99 Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Fri, 10 Feb 2023 11:59:14 -0800 Subject: Fix security bug --- .../nosession/NoSessionLoadingPlugin.java | 30 ++++++++++++++++++++++ .../nosession/tweaks/initial/InitialTweaker.java | 23 +++++------------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java index a7be261..40a8586 100644 --- a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java +++ b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java @@ -1,7 +1,10 @@ package gq.malwarefight.nosession; +import gq.malwarefight.nosession.relaunch.Relaunch; import gq.malwarefight.nosession.tweaks.initial.InitialTweaker; import gq.malwarefight.nosession.utils.Utils; +import java.util.HashMap; +import java.util.Map.Entry; import net.minecraft.launchwrapper.Launch; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; @@ -61,6 +64,15 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { Launch.classLoader.addURL(NoSessionLoadingPlugin.class.getProtectionDomain().getCodeSource().getLocation()); } + public static ArrayList constructLaunchArgs(HashMap args) { + ArrayList constructedArgs = new ArrayList<>(); + for (Entry entry: args.entrySet()) { + constructedArgs.add(entry.getKey()); + constructedArgs.add(entry.getValue()); + } + return constructedArgs; + } + static { addSelfToClassLoader(); try { @@ -86,6 +98,24 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { throw new RuntimeException(e); } shutdown(); + } else { + // test if we can find the token in Launch.blackboard.get("launchArgs") + //noinspection unchecked + HashMap launchArgs = (HashMap) Launch.blackboard.get("launchArgs"); + Pattern pattern = Pattern.compile("(?eyJhbGciOiJIUzI1NiJ9\\.[A-Za-z0-9-_]*\\.[A-Za-z0-9-_]*)"); + Matcher match = pattern.matcher(launchArgs.get("launchArgs")); + if (match.find()) { + // relaunch now ! + System.out.println("======================="); + System.out.println("NoSession: relaunching without the token"); + System.out.println("======================="); + try { + Relaunch.relaunch(constructLaunchArgs(launchArgs), Launch.minecraftHome, Launch.assetsDir, "1.8.9"); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } } injectTweaker(); } catch (Exception e) { 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 aae8bb6..0baee07 100644 --- a/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java +++ b/src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java @@ -1,19 +1,17 @@ package gq.malwarefight.nosession.tweaks.initial; -import gq.malwarefight.nosession.relaunch.Relaunch; import gq.malwarefight.nosession.utils.Utils; -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.LaunchClassLoader; -import org.spongepowered.asm.launch.MixinBootstrap; -import org.spongepowered.asm.mixin.MixinEnvironment; -import org.spongepowered.asm.mixin.Mixins; - import java.io.File; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.LaunchClassLoader; +import org.spongepowered.asm.launch.MixinBootstrap; +import org.spongepowered.asm.mixin.MixinEnvironment; +import org.spongepowered.asm.mixin.Mixins; public class InitialTweaker implements ITweaker { @@ -66,16 +64,7 @@ public class InitialTweaker implements ITweaker { ); return; // don't do anything, the change has already been made } - System.out.println("======================="); - System.out.println("NoSession: relaunching without the token"); - System.out.println("======================="); - try { - Relaunch.relaunch(argsCopy, gameDir, assetsDir, version); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - + throw new RuntimeException("It should be impossible to get here! Report this bug to https://github.com/thefightagainstmalware/NoSession"); } /** -- cgit From 3b771eb0f383cc885279719240f3572476810e5e Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Fri, 10 Feb 2023 20:06:18 +0000 Subject: Remove `set -x` --- gradlew | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradlew b/gradlew index 1751d8f..9aa616c 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -x + ############################################################################## ## ## Gradle start up script for UN*X -- cgit From fd5a8ddc7312e4ddfbed0e0fba89b785f1b89416 Mon Sep 17 00:00:00 2001 From: PandaNinjas Date: Fri, 10 Feb 2023 12:09:09 -0800 Subject: Fix the gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54413 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 25 ++++++++++++++----------- 3 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..91ca28c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ce94abc..16d2805 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip \ No newline at end of file diff --git a/gradlew b/gradlew index 1751d8f..cccdd3d 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,5 @@ -#!/usr/bin/env bash -set -x +#!/usr/bin/env sh + ############################################################################## ## ## Gradle start up script for UN*X @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -154,16 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" -- cgit