aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/artifacts/CopyMod.xml8
-rw-r--r--.idea/artifacts/CopyResources.xml8
-rw-r--r--README.md4
-rw-r--r--SECURITY.md7
-rw-r--r--build.gradle35
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin52928 -> 54413 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rwxr-xr-xgradlew23
-rw-r--r--src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java126
-rw-r--r--src/main/java/gq/malwarefight/nosession/mixin/BlankTweaker.java29
-rw-r--r--src/main/java/gq/malwarefight/nosession/mixin/InitialTweaker.java209
-rw-r--r--src/main/java/gq/malwarefight/nosession/mixin/L2Tweaker.java68
-rw-r--r--src/main/java/gq/malwarefight/nosession/mixin/Utils.java122
-rw-r--r--src/main/java/gq/malwarefight/nosession/mixin/asm/ReplacingMethodVisitor.java22
-rw-r--r--src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java12
-rw-r--r--src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java105
-rw-r--r--src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java83
-rw-r--r--src/main/java/gq/malwarefight/nosession/tweaks/cleanup/package-info.java5
-rw-r--r--src/main/java/gq/malwarefight/nosession/tweaks/initial/InitialTweaker.java97
-rw-r--r--src/main/java/gq/malwarefight/nosession/tweaks/initial/package-info.java5
-rw-r--r--src/main/java/gq/malwarefight/nosession/utils/Utils.java186
-rw-r--r--src/main/java/gq/malwarefight/tokenapp/Main.java21
-rw-r--r--src/main/java/gq/malwarefight/tokenapp/SocketThread.java10
-rw-r--r--src/main/resources/mixins.nosession.json2
24 files changed, 669 insertions, 520 deletions
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 @@
-<component name="ArtifactManager">
- <artifact name="CopyMod">
- <output-path>$USER_HOME$/AppData/Roaming/.minecraft/mods</output-path>
- <root id="root">
- <element id="dir-copy" path="$PROJECT_DIR$/build/libs" />
- </root>
- </artifact>
-</component> \ 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 @@
-<component name="ArtifactManager">
- <artifact name="CopyResources">
- <output-path>$PROJECT_DIR$/build/classes/main</output-path>
- <root id="root">
- <element id="dir-copy" path="$PROJECT_DIR$/src/main/resources" />
- </root>
- </artifact>
-</component> \ No newline at end of file
diff --git a/README.md b/README.md
index 57750c5..f707b93 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.<br>
+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).
+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..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.<br>
If you would like, you can encrypt the message with my [public GPG key](https://raw.githubusercontent.com/pandaninjas/pandaninjas/main/pandaninjas-publickey.key)<br>
-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
diff --git a/build.gradle b/build.gradle
index e6d4434..c54ce08 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,13 +13,12 @@ buildscript {
plugins {
id "java"
- 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"
+project.version = "1.1.0-dev"
group = "gq.malwarefight.nosession"
archivesBaseName = "nosession"
@@ -27,11 +26,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 {
@@ -46,13 +44,15 @@ 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'
exclude module: 'commons-io'
exclude module: 'log4j-core'
}
+
+ compileOnly "net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9:universal"
}
processResources {
@@ -72,26 +72,23 @@ 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,
"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
index 6ffa237..91ca28c 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7b64320..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-3.1-bin.zip
diff --git a/gradlew b/gradlew
index 9aa616c..cccdd3d 100755
--- a/gradlew
+++ b/gradlew
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
##############################################################################
##
@@ -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" "$@"
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..40a8586
--- /dev/null
+++ b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java
@@ -0,0 +1,126 @@
+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;
+
+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.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) {}
+
+ @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 ArrayList<String> constructLaunchArgs(HashMap<String, String> args) {
+ ArrayList<String> constructedArgs = new ArrayList<>();
+ for (Entry<String, String> entry: args.entrySet()) {
+ constructedArgs.add(entry.getKey());
+ constructedArgs.add(entry.getValue());
+ }
+ return constructedArgs;
+ }
+
+ static {
+ 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();
+ } else {
+ // test if we can find the token in Launch.blackboard.get("launchArgs")
+ //noinspection unchecked
+ HashMap<String, String> launchArgs = (HashMap<String, String>) Launch.blackboard.get("launchArgs");
+ Pattern pattern = Pattern.compile("(?<token>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) {
+ 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..ff07cab 100644
--- a/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java
+++ b/src/main/java/gq/malwarefight/nosession/mixin/client/YggdrasilSessionMixin.java
@@ -3,7 +3,7 @@ package gq.malwarefight.nosession.mixin.client;
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;
@@ -20,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("<noSessionAccessToken>")) { // 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));
@@ -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/nosession/relaunch/Relaunch.java b/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java
new file mode 100644
index 0000000..e712254
--- /dev/null
+++ b/src/main/java/gq/malwarefight/nosession/relaunch/Relaunch.java
@@ -0,0 +1,105 @@
+package gq.malwarefight.nosession.relaunch;
+
+import gq.malwarefight.nosession.tweaks.cleanup.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.MalformedURLException;
+import java.net.URISyntaxException;
+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>());
+ resetTransformerWrapper();
+ addSelfToClassloader();
+ Class<Launch> innerLaunch = Launch.class;
+ Method launch = innerLaunch.getDeclaredMethod("main", String[].class);
+ launch.invoke(null, (Object) constructArgs(args, gameDir, assetsDir, version));
+ }
+
+ private 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(CleanupTweaker.class.getName());
+ return initial.toArray(new String[0]);
+ }
+
+ 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);
+ for (Field field: fields) {
+ if (field.getType().equals(SecurityManager.class)) {
+ field.setAccessible(true);
+ field.set(null, null);
+ }
+ }
+ }
+
+
+ private static void resetTransformerWrapper() throws IllegalAccessException, NoSuchFieldException {
+ Field[] fields = ASMTransformerWrapper.class.getDeclaredFields();
+ for (Field field: fields) {
+ if ((field.getModifiers() & Modifier.STATIC) != 0) {
+ setToNull(field);
+ }
+ }
+ }
+
+ private static void setToNull(Class<?> cls, String fieldname) throws NoSuchFieldException, IllegalAccessException {
+ Field f = cls.getDeclaredField(fieldname);
+ setToNull(f);
+ }
+
+ 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");
+ 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);
+ }
+ }
+
+ 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/cleanup/CleanupTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java
new file mode 100644
index 0000000..a0d458b
--- /dev/null
+++ b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java
@@ -0,0 +1,83 @@
+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<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>());
+ //noinspection UnstableApiUsage
+ 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/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..0baee07
--- /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.utils.Utils;
+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 {
+
+ 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) {
+ boolean isRelaunch = false; // is this invocation from a relaunch?
+ String uuid = null;
+ 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>")) {
+ isRelaunch = true;
+ }
+ try {
+ Utils.setToken(args.get(i + 1));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ argsCopy.set(i + 1, "<noSessionAccessToken>");
+ } 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(Utils.normalizeUUID(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
+ }
+ throw new RuntimeException("It should be impossible to get here! Report this bug to https://github.com/thefightagainstmalware/NoSession");
+ }
+
+ /**
+ * 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
new file mode 100644
index 0000000..b34c0aa
--- /dev/null
+++ b/src/main/java/gq/malwarefight/nosession/utils/Utils.java
@@ -0,0 +1,186 @@
+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.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.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.Paths;
+import java.util.Arrays;
+import java.util.Properties;
+import java.util.UUID;
+
+public class Utils {
+ public static int PORT = -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(UUID id) {
+ if (PORT == -1) {
+ Socket socket = null;
+ 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("uuid\n".getBytes(StandardCharsets.UTF_8));
+ String value = readString(socket.getInputStream(), '\n');
+ if (UUID.fromString(value).equals(id)) {
+ port = i;
+ PORT = port;
+ break;
+ }
+ } catch (Exception exception) {
+ socket = null;
+ }
+ }
+ return socket;
+ } else {
+ try {
+ Socket socket = new Socket();
+ socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), PORT));
+ return socket;
+ } catch (IOException e) {
+ PORT = -1;
+ return getProperSocket(id);
+ }
+ }
+ }
+
+ 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);
+ 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 uri) {
+ try {
+ return uri.substring(uri.indexOf('/'), uri.lastIndexOf('!'));
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 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 new File(uri);
+ }
+
+ 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 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 {
+ Properties p = getJavaProperties();
+ ProcessBuilder processBuilder = new ProcessBuilder(
+ getJavaExe(p), "-cp", getClasspath(p), Main.class.getName()
+ );
+ 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..f86711b 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;
@@ -14,25 +14,25 @@ import java.net.*;
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');
- 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);
@@ -40,7 +40,6 @@ public class Main {
}
public static void main(String[] args) {
- ID = Long.parseLong(args[0]);
try {
setup();
} catch (Exception e) {
@@ -53,7 +52,9 @@ public class Main {
//noinspection resource
sock = new ServerSocket(i, 50, InetAddress.getLoopbackAddress());
break;
- } catch (Exception ignored) {}
+ } catch (Exception ignored) {
+ // we couldn't bind to the port, try the next one
+ }
}
if (sock == null) {
System.err.println("Could not bind to any valid port");
@@ -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 05db6f0..e0f69dc 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;
@@ -50,13 +50,15 @@ 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);
}
}
- } catch (IOException ignored) {}
+ } catch (IOException ignored) {
+ // if something goes wrong, don't worry! just quit immediately
+ }
}
}
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