diff options
-rw-r--r-- | build.gradle | 83 | ||||
-rw-r--r-- | gradle/wrapper/gradle-wrapper.properties | 2 | ||||
-rw-r--r-- | src/main/cpp/libc.cpp | 31 | ||||
-rw-r--r-- | src/main/cpp/libc.h | 29 | ||||
-rw-r--r-- | src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java | 111 | ||||
-rw-r--r-- | src/main/java/gq/malwarefight/nosession/linux/bwrap/BubblewrapBuilder.java | 401 | ||||
-rw-r--r-- | src/main/java/gq/malwarefight/nosession/linux/libc/Libc.java | 31 | ||||
-rw-r--r-- | src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java | 1 | ||||
-rw-r--r-- | src/main/java/gq/malwarefight/nosession/utils/Utils.java | 55 |
9 files changed, 672 insertions, 72 deletions
diff --git a/build.gradle b/build.gradle index 69efcb6..9d62ac3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,24 +1,31 @@ +import org.gradle.internal.jvm.Jvm + buildscript { + ext.kotlin_version = '1.8.21' repositories { //new forge repository maven { url "https://maven.minecraftforge.net" } - maven { url 'https://repo.spongepowered.org/maven'} + maven { url 'https://jitpack.io/' } mavenCentral() + mavenLocal() } dependencies { - classpath "net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT" - classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT' + classpath 'com.github.thefightagainstmalware:ForgeGradle:5a1fcb9' + classpath 'com.github.thefightagainstmalware:MixinGradle:92e66fe' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } plugins { id "java" + id 'cpp' } apply plugin: 'net.minecraftforge.gradle.forge' +apply plugin: 'kotlin' apply plugin: 'org.spongepowered.mixin' -project.version = "1.1.1" +project.version = "1.2.0" group = "gq.malwarefight.nosession" archivesBaseName = "nosession" @@ -44,6 +51,7 @@ repositories { } dependencies { + compileOnly "net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9:universal" shade('org.spongepowered:mixin:0.7.11-SNAPSHOT') { exclude module: 'launchwrapper' exclude module: 'guava' @@ -52,10 +60,13 @@ dependencies { exclude module: 'log4j-core' } - compileOnly "net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9:universal" + compileOnly 'org.spongepowered:mixin:0.7.11-SNAPSHOT' // why gradle why why why + annotationProcessor 'org.spongepowered:mixin:0.8.5:processor' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" } processResources { + duplicatesStrategy = DuplicatesStrategy.WARN //replace stuff in mcmod.info, nothing else from(sourceSets.main.resources.srcDirs) { include "mcmod.info" @@ -73,15 +84,64 @@ processResources { } +sourceSets { + main { + ext.refMap = "mixins.nosession.refmap.json" + } +} + +model { + components { + linux(NativeLibrarySpec) { + sources { + cpp { + source { + srcDir 'src/main/cpp' + include "libc.cpp" + } + } + } + buildTypes { + release + } + + binaries.all { + def jvmHome = Jvm.current().javaHome + if (targetPlatform.operatingSystem.macOsX) { + cppCompiler.args '-I', "${jvmHome}/include" + cppCompiler.args '-I', "${jvmHome}/include/darwin" + cppCompiler.args '-mmacosx-version-min=10.4' + linker.args '-mmacosx-version-min=10.4' + } else if (targetPlatform.operatingSystem.linux) { + cppCompiler.args '-I', "${jvmHome}/include" + cppCompiler.args '-I', "${jvmHome}/include/linux" + cppCompiler.args '-D_FILE_OFFSET_BITS=64' + } else if (targetPlatform.operatingSystem.windows) { + cppCompiler.args "-I${jvmHome}/include" + cppCompiler.args "-I${jvmHome}/include/win32" + } else if (targetPlatform.operatingSystem.freeBSD) { + cppCompiler.args '-I', "${jvmHome}/include" + cppCompiler.args '-I', "${jvmHome}/include/freebsd" + } + } + } + } +} + + jar { - dependsOn configurations.compile + dependsOn project.configurations.compile + inputs.files {linuxSharedLibrary} + from("build/libs/linux/shared/" + System.mapLibraryName("linux")) { + into("native/" + System.getProperty("os.arch") + "/" + System.getProperty("os.name")) + } + from { - configurations.compile.collect { + project.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, @@ -91,9 +151,6 @@ jar { "FMLCorePlugin": "gq.malwarefight.nosession.NoSessionLoadingPlugin" } } - -sourceSets { - main { - ext.refMap = "mixins.nosession.refmap.json" - } +kotlin { + jvmToolchain(11) } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 16d2805..da1db5f 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 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/cpp/libc.cpp b/src/main/cpp/libc.cpp new file mode 100644 index 0000000..060e83e --- /dev/null +++ b/src/main/cpp/libc.cpp @@ -0,0 +1,31 @@ +#include <jni.h> +#include "unistd.h" +#include <fcntl.h> +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: gq_malwarefight_nosession_linux_libc_Libc + * Method: geteuid + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_gq_malwarefight_nosession_linux_libc_Libc_geteuid + (JNIEnv *, jclass) { + return (int) geteuid(); +} + +/* + * Class: gq_malwarefight_nosession_linux_libc_Libc + * Method: unlink + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT void JNICALL Java_gq_malwarefight_nosession_linux_libc_Libc_unlink + (JNIEnv* env, jclass, jstring string) { + const char* path = env->GetStringUTFChars(string, NULL); + unlink(path); + env->ReleaseStringUTFChars(string, path); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/main/cpp/libc.h b/src/main/cpp/libc.h new file mode 100644 index 0000000..6aaa1a6 --- /dev/null +++ b/src/main/cpp/libc.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class gq_malwarefight_nosession_linux_libc_Libc */ + +#ifndef _Included_gq_malwarefight_nosession_linux_libc_Libc +#define _Included_gq_malwarefight_nosession_linux_libc_Libc // NOLINT(bugprone-reserved-identifier) +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: gq_malwarefight_nosession_linux_libc_Libc + * Method: geteuid + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_gq_malwarefight_nosession_linux_libc_Libc_geteuid + (JNIEnv *, jclass); + +/* + * Class: gq_malwarefight_nosession_linux_libc_Libc + * Method: unlink + * Signature: (Ljava/lang/String;) + */ +JNIEXPORT void JNICALL Java_gq_malwarefight_nosession_linux_libc_Libc_unlink + (JNIEnv *, jclass, jstring); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java index b61665f..9e0eb27 100644 --- a/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java +++ b/src/main/java/gq/malwarefight/nosession/NoSessionLoadingPlugin.java @@ -1,24 +1,28 @@ package gq.malwarefight.nosession; +import gq.malwarefight.nosession.linux.bwrap.BubblewrapBuilder; +import gq.malwarefight.nosession.linux.libc.Libc; import gq.malwarefight.nosession.relaunch.Relaunch; import gq.malwarefight.nosession.tweaks.initial.InitialTweaker; import gq.malwarefight.nosession.utils.Utils; +import net.minecraft.launchwrapper.Launch; +import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; +import org.apache.commons.lang3.SystemUtils; +import org.apache.logging.log4j.LogManager; + +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.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.minecraft.launchwrapper.Launch; -import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; +@SuppressWarnings("unused") @IFMLLoadingPlugin.MCVersion("1.8.9") @IFMLLoadingPlugin.Name("NoSession trolling") @IFMLLoadingPlugin.SortingIndex(0) @@ -39,20 +43,14 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { } @Override - public void injectData(Map<String, Object> map) {} + 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"); @@ -65,7 +63,7 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { public static ArrayList<String> constructLaunchArgs(HashMap<String, String> args) { ArrayList<String> constructedArgs = new ArrayList<>(); - for (Entry<String, String> entry: args.entrySet()) { + for (Entry<String, String> entry : args.entrySet()) { if (entry.getKey().equals("--accessToken")) { constructedArgs.add(entry.getKey()); constructedArgs.add("<noSessionAccessToken>"); @@ -77,31 +75,75 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { return constructedArgs; } + public static void relaunchProcess(Matcher m) throws Exception { + RuntimeMXBean rmb = ManagementFactory.getRuntimeMXBean(); + if (!SystemUtils.IS_OS_LINUX) { + ArrayList<String> args = new ArrayList<>(); + args.add(Utils.getJavaExe()); + args.add("-cp"); + args.add(System.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); + } + } else { + BubblewrapBuilder builder = new BubblewrapBuilder() + .setCommand(Utils.getJavaExe()) + .unshareAll() + .shareNet() + .setHostname("nosession-on-top") + .readOnlyBind("/usr", "/usr") + .mountTmpfs("/tmp") + .mountTmpfs("/var") + .mountProc("/proc") + .mountDev("/dev") + .readOnlyBind("/etc/resolv.conf", "/etc/resolv.conf") + .addSymlink("usr/lib", "/lib") + .addSymlink("usr/lib64", "/lib64") + .addSymlink("usr/bin", "/bin") + .addSymlink("usr/sbin", "/sbin") + .createDir("/run/user/" + Libc.geteuid()) + .setEnv("XDG_RUNTIME_DIR", "/run/user/" + Libc.geteuid()) + .readOnlyBind(System.getProperty("java.home"), System.getProperty("java.home")) + .bind(Launch.minecraftHome.getAbsolutePath(), Launch.minecraftHome.getAbsolutePath()) + .bind(Launch.assetsDir.getAbsolutePath(), Launch.assetsDir.getAbsolutePath()) + .readOnlyBind(Utils.getLibraryPathAsFile(NoSessionLoadingPlugin.class).getAbsolutePath(), Utils.getLibraryPathAsFile(NoSessionLoadingPlugin.class).getAbsolutePath()); + if (System.getenv("XDG_SESSION_TYPE").equals("x11")) { + LogManager.getLogger().warn("X11 detected, enabling X11 passthrough, which could allow keyloggers to work"); + } + builder.bind("/tmp/.X11-unix/X" + System.getenv("DISPLAY").substring(1), "/tmp/.X11-unix/X0") + .setEnv("DISPLAY", ":0"); + // add all the entries on the classpath read only + String classPath = rmb.getClassPath(); + for (String path : classPath.split(System.getProperty("path.separator"))) { + File lib = new File(path); + builder.readOnlyBind(lib.getAbsolutePath(), lib.getAbsolutePath()); + } + builder.addArgs("-cp", rmb.getClassPath(), "net.minecraft.launchwrapper.Launch"); + // blackboard erases the type, but we are sure that launchArgs will always be a HashMap<String, String> + //noinspection unchecked + builder.addArgs(constructLaunchArgs((HashMap<String, String>) Launch.blackboard.get("launchArgs"))); + builder.addArgs("--tweakClass", "net.minecraftforge.fml.common.launcher.FMLTweaker"); + builder.build().start(); + } + Utils.shutdown(); + } + 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")); + Matcher m = mcJWT.matcher(System.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(); + relaunchProcess(m); } else { // test if we can find the token in Launch.blackboard.get("launchArgs") //noinspection unchecked @@ -114,6 +156,9 @@ public class NoSessionLoadingPlugin implements IFMLLoadingPlugin { System.out.println("======================="); System.out.println("NoSession: relaunching without the token"); System.out.println("======================="); + if (SystemUtils.IS_OS_LINUX) { + relaunchProcess(m); + } try { Relaunch.relaunch(constructLaunchArgs(launchArgs)); } catch (Exception e) { diff --git a/src/main/java/gq/malwarefight/nosession/linux/bwrap/BubblewrapBuilder.java b/src/main/java/gq/malwarefight/nosession/linux/bwrap/BubblewrapBuilder.java new file mode 100644 index 0000000..34542cd --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/linux/bwrap/BubblewrapBuilder.java @@ -0,0 +1,401 @@ +package gq.malwarefight.nosession.linux.bwrap; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import gq.malwarefight.nosession.linux.libc.Libc; +import gq.malwarefight.nosession.utils.Utils; +import org.apache.logging.log4j.LogManager; + +import javax.net.ssl.HttpsURLConnection; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; + +@SuppressWarnings("unused") // library class +public class BubblewrapBuilder { + final ArrayList<String> bubblewrapArgs = new ArrayList<>(); + final ArrayList<String> programArgs = new ArrayList<>(); + String command = ""; + + public BubblewrapBuilder addArgsFromFD(int fd) { + bubblewrapArgs.add("--args"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder unshareAll() { + bubblewrapArgs.add("--unshare-all"); + return this; + } + + public BubblewrapBuilder shareNet() { + bubblewrapArgs.add("--share-net"); + return this; + } + + public BubblewrapBuilder unshareUser() { + return unshareUser(false); + } + + public BubblewrapBuilder unshareUser(boolean try_) { + bubblewrapArgs.add(try_ ? "--unshare-user-try" : "--unshare-user"); + return this; + } + + public BubblewrapBuilder unshareIPC() { + bubblewrapArgs.add("--unshare-ipc"); + return this; + } + + public BubblewrapBuilder unsharePID() { + bubblewrapArgs.add("--unshare-pid"); + return this; + } + + public BubblewrapBuilder unshareNet() { + bubblewrapArgs.add("--unshare-net"); + return this; + } + + public BubblewrapBuilder unshareUTS() { + bubblewrapArgs.add("--unshare-uts"); + return this; + } + + public BubblewrapBuilder unshareCGroup() { + return unshareCGroup(false); + } + + public BubblewrapBuilder unshareCGroup(boolean try_) { + bubblewrapArgs.add(try_ ? "--unshare-cgroup-try" : "--unshare-cgroup"); + return this; + } + + public BubblewrapBuilder setUserNS(int fd) { + return setUserNS(fd, false); + } + + public BubblewrapBuilder setUserNS(int fd, boolean afterSetup) { + bubblewrapArgs.add(afterSetup ? "--userns2" : "--userns"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder disableUserNS() { + bubblewrapArgs.add("--disable-userns"); + return this; + } + + public BubblewrapBuilder assertUserNSDisabled() { + bubblewrapArgs.add("--assert-userns-disabled"); + return this; + } + + public BubblewrapBuilder setPIDNS(int fd) { + bubblewrapArgs.add("--pidns"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder setUID(int uid) { + bubblewrapArgs.add("--uid"); + bubblewrapArgs.add(String.valueOf(uid)); + return this; + } + + public BubblewrapBuilder setGID(int gid) { + bubblewrapArgs.add("--gid"); + bubblewrapArgs.add(String.valueOf(gid)); + return this; + } + + public BubblewrapBuilder setHostname(String name) { + bubblewrapArgs.add("--hostname"); + bubblewrapArgs.add(name); + return this; + } + + public BubblewrapBuilder setChdir(String dir) { + bubblewrapArgs.add("--chdir"); + bubblewrapArgs.add(dir); + return this; + } + + public BubblewrapBuilder clearEnv() { + bubblewrapArgs.add("--clearenv"); + return this; + } + + public BubblewrapBuilder setEnv(String key, String value) { + bubblewrapArgs.add("--setenv"); + bubblewrapArgs.add(key); + bubblewrapArgs.add(value); + return this; + } + + public BubblewrapBuilder unSetEnv(String key) { + bubblewrapArgs.add("--unsetenv"); + bubblewrapArgs.add(key); + return this; + } + + public BubblewrapBuilder setLockFile(String file) { + bubblewrapArgs.add("--lock-file"); + bubblewrapArgs.add(file); + return this; + } + + public BubblewrapBuilder syncFD(int fd) { + bubblewrapArgs.add("--sync-fd"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder bind(String src, String dest) { + return bind(src, dest, false, false, false); + } + + public BubblewrapBuilder bindTry(String src, String dest) { + return bind(src, dest, true, false, false); + } + + public BubblewrapBuilder devBind(String src, String dest) { + return bind(src, dest, false, true, false); + } + + public BubblewrapBuilder devBindTry(String src, String dest) { + return bind(src, dest, true, true, false); + } + + public BubblewrapBuilder readOnlyBind(String src, String dest) { + return bind(src, dest, false, false, true); + } + + public BubblewrapBuilder readOnlyBindTry(String src, String dest) { + return bind(src, dest, true, false, true); + } + + private BubblewrapBuilder bind(String src, String dest, boolean try_, boolean dev, boolean ro) { + if (ro && dev) { + throw new IllegalArgumentException("Cannot bind both read-only and dev"); + } + StringBuilder argument = new StringBuilder(); + argument.append("--"); + if (dev) { + argument.append("dev-"); + } else if (ro) { + argument.append("ro-"); + } + argument.append("bind"); + if (try_) { + argument.append("-try"); + } + bubblewrapArgs.add(argument.toString()); + bubblewrapArgs.add(src); + bubblewrapArgs.add(dest); + return this; + } + + public BubblewrapBuilder readOnlyRemount(String dest) { + bubblewrapArgs.add("--remount-ro"); + bubblewrapArgs.add(dest); + return this; + } + + public BubblewrapBuilder execLabel(String label) { + bubblewrapArgs.add("--exec-label"); + bubblewrapArgs.add(label); + return this; + } + + public BubblewrapBuilder fileLabel(String label) { + bubblewrapArgs.add("--file-label"); + bubblewrapArgs.add(label); + return this; + } + + public BubblewrapBuilder mountProc(String dest) { + bubblewrapArgs.add("--proc"); + bubblewrapArgs.add(dest); + return this; + } + + + public BubblewrapBuilder mountDev(String dest) { + bubblewrapArgs.add("--dev"); + bubblewrapArgs.add(dest); + return this; + } + + public BubblewrapBuilder mountTmpfs(String dest) { + bubblewrapArgs.add("--tmpfs"); + bubblewrapArgs.add(dest); + return this; + } + + public BubblewrapBuilder mountMqueue(String dest) { + bubblewrapArgs.add("--mqueue"); + bubblewrapArgs.add(dest); + return this; + } + + public BubblewrapBuilder createDir(String dest) { + bubblewrapArgs.add("--dir"); + bubblewrapArgs.add(dest); + return this; + } + + + public BubblewrapBuilder copyFile(int fd, String dest) { + bubblewrapArgs.add("--file"); + bubblewrapArgs.add(String.valueOf(fd)); + bubblewrapArgs.add(dest); + return this; + } + + + public BubblewrapBuilder bindData(int fd, String dest) { + return bindData(fd, dest, false); + } + + + public BubblewrapBuilder bindDataReadOnly(int fd, String dest) { + return bindData(fd, dest, true); + } + + private BubblewrapBuilder bindData(int fd, String dest, boolean ro) { + bubblewrapArgs.add("--" + (ro ? "ro" : "") + "bind-data"); + bubblewrapArgs.add(String.valueOf(fd)); + bubblewrapArgs.add(dest); + return this; + } + + public BubblewrapBuilder addSymlink(String src, String target) { + bubblewrapArgs.add("--symlink"); + bubblewrapArgs.add(src); + bubblewrapArgs.add(target); + return this; + } + + public BubblewrapBuilder loadSeccompFilters(int fd) { + bubblewrapArgs.add("--seccomp"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder addSeccompFilters(int fd) { + bubblewrapArgs.add("--add-seccomp-fd"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + + public BubblewrapBuilder blockOnFD(int fd) { + bubblewrapArgs.add("--block-fd"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder blockOnFdUntilUserNS(int fd) { + bubblewrapArgs.add("--userns-block-fd"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder infoFD(int fd) { + bubblewrapArgs.add("--info-fd"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder jsonStatusFD(int fd) { + bubblewrapArgs.add("--json-status-fd"); + bubblewrapArgs.add(String.valueOf(fd)); + return this; + } + + public BubblewrapBuilder newSession() { + bubblewrapArgs.add("--new-session"); + return this; + } + + public BubblewrapBuilder dieWithParent() { + bubblewrapArgs.add("--die-with-parent"); + return this; + } + + public BubblewrapBuilder asPid1() { + bubblewrapArgs.add("--as-pid-1"); + return this; + } + + public BubblewrapBuilder capAdd(String capability) { + bubblewrapArgs.add("--cap-add"); + bubblewrapArgs.add(capability); + return this; + } + + public BubblewrapBuilder capDrop(String capability) { + bubblewrapArgs.add("--cap-drop"); + bubblewrapArgs.add(capability); + return this; + } + + public BubblewrapBuilder chmod(String octal, String path) { + bubblewrapArgs.add("--chmod"); + bubblewrapArgs.add(octal); + bubblewrapArgs.add(path); + return this; + } + + public BubblewrapBuilder addArgs(Iterable<String> args) { + args.forEach(programArgs::add); + return this; + } + + public BubblewrapBuilder addArgs(String... args) { + programArgs.addAll(Arrays.asList(args)); + return this; + } + + public BubblewrapBuilder setCommand(String command) { + this.command = command; + return this; + } + + private String downloadBubblewrap() throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + File f = Files.createTempFile("nosession_bwrap", "").toFile(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + Libc.unlink(f.getAbsolutePath()); + })); + URL url = new URL("https://api.github.com/repos/pandaninjas/bubblewrap-static/releases/latest"); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + String result = Utils.readString(connection.getInputStream(), null); + JsonObject object = new JsonParser().parse(result).getAsJsonObject(); + URL downloadURL = new URL(object.get("assets").getAsJsonArray().get(0).getAsJsonObject().get("browser_download_url").getAsString()); + HttpsURLConnection download = (HttpsURLConnection) downloadURL.openConnection(); + Utils.copy(download.getInputStream(), Files.newOutputStream(f.toPath())); + boolean success = f.setExecutable(true); + if (!success) { + LogManager.getLogger().error("Failed to mark file as executable. Quitting"); + Utils.shutdown(); + } + return f.getAbsolutePath(); + } + + public ProcessBuilder build() throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + ProcessBuilder pb = new ProcessBuilder(); + String bubbleWrapPath = downloadBubblewrap(); + ArrayList<String> finalArgs = new ArrayList<>(); + finalArgs.add(bubbleWrapPath); + finalArgs.addAll(bubblewrapArgs); + finalArgs.add(command); + finalArgs.addAll(programArgs); + pb.command(finalArgs).redirectOutput(new File("/home/pandaninjas/log")).redirectError(new File("/home/pandaninjas/log1")); + return pb; + } +} diff --git a/src/main/java/gq/malwarefight/nosession/linux/libc/Libc.java b/src/main/java/gq/malwarefight/nosession/linux/libc/Libc.java new file mode 100644 index 0000000..670f83d --- /dev/null +++ b/src/main/java/gq/malwarefight/nosession/linux/libc/Libc.java @@ -0,0 +1,31 @@ +package gq.malwarefight.nosession.linux.libc; + +import gq.malwarefight.nosession.utils.Utils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; + +public class Libc { + private static final Logger log = LogManager.getLogger(); + public static native int geteuid(); + public static native void unlink(String pathname); + + static { + try { + File tempFile = Files.createTempFile("nosession_libc", ".so").toFile(); + try (InputStream is = Libc.class.getResourceAsStream("/native/" + System.getProperty("os.arch") + "/" + System.getProperty("os.name") + "/" + System.mapLibraryName("linux"))) { + assert is != null: "Native library not compiled"; + Utils.copy(is, Files.newOutputStream(tempFile.toPath())); + } + System.load(tempFile.getAbsolutePath()); + Runtime.getRuntime().addShutdownHook( + new Thread(() -> Libc.unlink(tempFile.getAbsolutePath())) + ); + } catch (Exception e) { + log.error("Failed to load the native Linux modules, native libraries may fail", e); + } + } +} diff --git a/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java index a0d458b..9a5997b 100644 --- a/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java +++ b/src/main/java/gq/malwarefight/nosession/tweaks/cleanup/CleanupTweaker.java @@ -39,6 +39,7 @@ public class CleanupTweaker implements ITweaker { Utils.setStaticValue(cls, "wrapperParentMap", new HashMap<String, String>()); //noinspection UnstableApiUsage Utils.setStaticValue(cls, "wrapperCache", CacheBuilder.newBuilder().maximumSize(30L).weakValues().build(new CacheLoader<String, byte[]>() { + @SuppressWarnings("NullableProblems") public byte[] load(String file) throws Exception { return (byte[]) makeWrapper.invoke(null, file); } diff --git a/src/main/java/gq/malwarefight/nosession/utils/Utils.java b/src/main/java/gq/malwarefight/nosession/utils/Utils.java index ab2f9c0..7f65b2a 100644 --- a/src/main/java/gq/malwarefight/nosession/utils/Utils.java +++ b/src/main/java/gq/malwarefight/nosession/utils/Utils.java @@ -4,6 +4,7 @@ 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.linux.libc.Libc; import gq.malwarefight.tokenapp.Main; import org.apache.commons.io.ByteOrderMark; import org.apache.commons.lang3.CharEncoding; @@ -13,27 +14,33 @@ 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.io.*; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.URISyntaxException; +import java.net.*; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; -import java.util.Properties; +import java.util.Scanner; import java.util.UUID; public class Utils { public static int PORT = -1; private static final int BASE_PORT = 47777; + public static void copy(InputStream i, OutputStream o) throws IOException { + byte[] buffer = new byte[16384]; + int read; + while ((read = i.read(buffer)) > 0) { + o.write(buffer, 0, read); + } + i.close(); + o.close(); + } + public static byte[] read(InputStream i, Character delimiter) throws IOException { byte[] buffer = new byte[512]; int index = 0; @@ -112,7 +119,7 @@ public class Utils { public static String processString(String uri) { try { - return uri.substring(uri.indexOf('/'), uri.lastIndexOf('!')); + return uri.substring(uri.indexOf(":") + 1, uri.lastIndexOf('!')); } catch (Exception e) { e.printStackTrace(); return uri; @@ -124,16 +131,16 @@ public class Utils { if (uri.endsWith(".class")) { uri = processString(uri); // stupid reference to a class within a jar } - return new File(uri); + return new File(new URI(uri)); } public static String getLibraryPath(Class<?> c) throws URISyntaxException { return getLibraryPathAsFile(c).getAbsolutePath(); } - private static String getClasspath(Properties p) throws URISyntaxException { + private static String getClasspath() throws URISyntaxException { return String.join( - p.getProperty("path.separator"), + System.getProperty("path.separator"), getLibraryPath(Main.class), getLibraryPath(YggdrasilAuthenticationService.class), getLibraryPath(Gson.class), @@ -148,23 +155,15 @@ public class Utils { ); } - 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) { + public static String getJavaExe() { try { return Paths.get(String.join( - p.getProperty("file.separator"), - p.getProperty("java.home"), + System.getProperty("file.separator"), + System.getProperty("java.home"), "bin", "java" + (SystemUtils.IS_OS_WINDOWS ? ".exe" : "") )).toFile().getAbsolutePath(); @@ -173,8 +172,7 @@ public class Utils { } } - public static void setToken(String token) throws IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, URISyntaxException { - Properties p = getJavaProperties(); + public static void setToken(String token) throws IOException, URISyntaxException { ProcessBuilder processBuilder = new ProcessBuilder( getJavaExe(p), "-XX:+DisableAttachMechanism", "-cp", getClasspath(p), Main.class.getName() ); @@ -183,4 +181,11 @@ public class Utils { c.getOutputStream().write((token + "\n").getBytes(StandardCharsets.UTF_8)); c.getOutputStream().flush(); } + 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); + } + } |