diff options
19 files changed, 463 insertions, 639 deletions
@@ -9,7 +9,6 @@ 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.<br>
-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.<br>
This only protects you from other mods. There are fake verification sites that can steal your session ID through that method.<br>
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 c64722e..d9d5e6d 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.<br> diff --git a/build.gradle b/build.gradle index 5ececca..e6d4434 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.forge' +apply plugin: 'net.minecraftforge.gradle.tweaker-client' apply plugin: 'org.spongepowered.mixin' -version = "1.1.0-dev" +version = "1.0.0" group = "gq.malwarefight.nosession" archivesBaseName = "nosession" @@ -27,10 +27,11 @@ compileJava.sourceCompatibility = compileJava.targetCompatibility = 1.8 compileJava.options.encoding = "UTF-8" minecraft { - version = "1.8.9-11.15.1.2318-1.8.9" + version = "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 { @@ -52,8 +53,6 @@ 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 { @@ -91,8 +90,8 @@ jar { "TweakOrder": 0, "ModSide": "CLIENT", 'FMLCorePluginContainsFMLMod': true, - 'MixinConfigs': 'mixins.nosession.json', - "FMLCorePlugin": "gq.malwarefight.nosession.NoSessionLoadingPlugin" + 'TweakClass': 'gq.malwarefight.nosession.mixin.InitialTweaker', + 'MixinConfigs': 'mixins.nosession.json' } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..6ffa237 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 357df5f..7b64320 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-3.1-bin.zip @@ -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 deleted file mode 100644 index 3ccbff2..0000000 --- a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java +++ /dev/null @@ -1,116 +0,0 @@ -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<String, Object> 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<String> tweakClassList = (ArrayList<String>) 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 +(?<token>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<String> 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 <noSessionAccessToken>"); - 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 new file mode 100644 index 0000000..2c9109b --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/mixin/BlankTweaker.java @@ -0,0 +1,29 @@ +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<String> 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 new file mode 100644 index 0000000..51425fc --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/mixin/InitialTweaker.java @@ -0,0 +1,209 @@ +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<ITweaker> 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<String> args, File gameDir, File assetsDir, String profile) { + ArrayList<String> 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, "<noSessionAccessToken>"); + } + } + 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<ITweaker> tweakers = (ArrayList<ITweaker>) 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<String>) 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<String> argsFromOurTweakers = new ArrayList<>(); + + for (ITweaker i: allTweakers) { + argsFromOurTweakers.addAll(Arrays.asList(i.getLaunchArguments())); + } + + //noinspection unchecked + ArrayList<String> launchArgs = (ArrayList<String>) Launch.blackboard.get("ArgumentList"); + for (int i = 0; i < launchArgs.size(); i++) { + if (launchArgs.get(i).equals("--accessToken")) { + launchArgs.set(i + 1, "<noSessionAccessToken>"); + } + } + if (!launchArgs.contains("--accessToken")) { + argsFromOurTweakers.add("--accessToken"); + argsFromOurTweakers.add("<noSessionAccessToken>"); + } + + 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 new file mode 100644 index 0000000..8736ca4 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/mixin/L2Tweaker.java @@ -0,0 +1,68 @@ +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<ITweaker> allTweakers = new ArrayList<>(); + + @Override + @SuppressWarnings("unchecked") + public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) { + ArrayList<String> argsCopy = new ArrayList<>(args); + for (int i = 0; i < argsCopy.size(); i++) { + if (argsCopy.get(i).equals("--accessToken")) { + argsCopy.set(i + 1, "<noSessionAccessToken>"); + } + } + + boolean any = false; + ArrayList<ITweaker> tweakers = (ArrayList<ITweaker>) 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<String>) 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<String> 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 new file mode 100644 index 0000000..2aaec37 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/mixin/Utils.java @@ -0,0 +1,122 @@ +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 new file mode 100644 index 0000000..d8c3005 --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/mixin/asm/ReplacingMethodVisitor.java @@ -0,0 +1,22 @@ +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 5451c26..48327fe 100644 --- a/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java +++ b/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java @@ -1,10 +1,9 @@ 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.utils.Utils; +import gq.malwarefight.nosession.mixin.Utils; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -47,4 +46,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 deleted file mode 100644 index 55e907c..0000000 --- a/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java +++ /dev/null @@ -1,100 +0,0 @@ -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<String> 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<String>()); - 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<Launch> innerLaunch = (Class<Launch>) 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<String> 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 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<String> args, File gameDir, File assetsDir, String profile) { - // add the FMLTweaker - ArrayList<String> tweakerList = (ArrayList<String>) Launch.blackboard.get("TweakClasses"); - tweakerList.add("net.minecraftforge.fml.common.launcher.FMLTweaker"); - // reset ASMTransformerWrapper - Class<ASMTransformerWrapper> cls = ASMTransformerWrapper.class; - try { - Utils.setStaticValue(cls, "wrapperModMap", new HashMap<String, String>()); - Utils.setStaticValue(cls, "wrapperParentMap", new HashMap<String, String>()); - Utils.setStaticValue(cls, "wrapperCache", CacheBuilder.newBuilder().maximumSize(30L).weakValues().build(new CacheLoader<String, byte[]>() { - 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<ASMTransformerWrapper> 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<? extends URLStreamHandler> constructor = ((Constructor<? extends URLStreamHandler>[]) 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<String> args, File gameDir, File assetsDir, String version) { - ArrayList<String> argsCopy = new ArrayList<>(args); - for (int i = 0; i < argsCopy.size(); i++) { - if (argsCopy.get(i).equals("--accessToken")) { - if (argsCopy.get(i + 1).equals("<noSessionAccessToken>")) { - 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, "<noSessionAccessToken>"); - } - } - 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 deleted file mode 100644 index fbacb8f..0000000 --- a/src/main/java/gq/malwarefight/nosession/utils/Utils.java +++ /dev/null @@ -1,221 +0,0 @@ -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 62a4da3..66afad1 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.utils.Utils; +import gq.malwarefight.nosession.mixin.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 de029c1..05db6f0 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.utils.Utils; +import gq.malwarefight.nosession.mixin.Utils; import java.io.IOException; import java.io.InputStream; |