diff options
Diffstat (limited to 'src/main/java/gq/malwarefight/nosession')
11 files changed, 626 insertions, 452 deletions
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<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 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<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 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<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 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<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 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<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 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<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 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<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 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(); + } +} |