diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index bc73674..33f3a7a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -28,4 +28,4 @@ jobs: uses: actions/upload-artifact@v3.1.0 with: name: Skidfuscator.jar - path: staging/client-2.0.0-SNAPSHOT.jar + path: staging/client-standalone-all.jar diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java index d7fdd22..9a306b7 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java @@ -1,7 +1,9 @@ package dev.skidfuscator.obfuscator; import dev.skidfuscator.obfuscator.command.HelpCommand; +import dev.skidfuscator.obfuscator.command.MappingsCommand; import dev.skidfuscator.obfuscator.command.ObfuscateCommand; +import dev.skidfuscator.obfuscator.util.LogoUtil; import lombok.SneakyThrows; import org.jline.reader.EndOfFileException; import org.jline.reader.LineReader; @@ -70,8 +72,10 @@ public static void main(String[] args) { } } else { + LogoUtil.printLogo(); new CommandLine(new HelpCommand()) .addSubcommand("obfuscate", new ObfuscateCommand()) + .addSubcommand("mappings", new MappingsCommand()) .execute(args); } } diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/command/MappingsCommand.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/command/MappingsCommand.java new file mode 100644 index 0000000..90cf8ff --- /dev/null +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/command/MappingsCommand.java @@ -0,0 +1,82 @@ +package dev.skidfuscator.obfuscator.command; + +import dev.skidfuscator.jghost.GhostHelper; +import dev.skidfuscator.jghost.tree.GhostLibrary; +import dev.skidfuscator.obfuscator.Skidfuscator; +import dev.skidfuscator.obfuscator.util.misc.SkidTimedLogger; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import picocli.CommandLine; + +import java.io.File; +import java.util.Arrays; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; + +@CommandLine.Command( + aliases = "mappings", + mixinStandardHelpOptions = true, + version = "1.0.0", + description = "Creates a collated mappings file given a specific directory" +) +public class MappingsCommand implements Callable { + + @CommandLine.Parameters( + index = "0", + description = "The directory which will be used to create the mappings file" + ) + private File input; + + @CommandLine.Option( + names = {"-o", "--output"}, + description = "Path to the output mappings file location" + ) + private File output = new File("compressed-mappings.json"); + + @Override + public Integer call() throws Exception { + if (input == null) { + System.out.println("Invalid input file"); + return 1; + } + + if (!input.getPath().endsWith(".jar") && !input.isDirectory()) { + System.err.println("Invalid input file. Must be a jar file or a directory"); + return 1; + } + + if (output == null) { + System.err.println("Invalid output file"); + return 1; + } + + final Logger log = LogManager.getLogger(Skidfuscator.class); + final SkidTimedLogger logger = new SkidTimedLogger(true, log); + + AtomicReference ghostLibrary = new AtomicReference<>(null); + iterateFolder(input, logger, ghostLibrary); + + GhostHelper.saveLibraryFile(logger, ghostLibrary.get(), output); + logger.style("Successfully created mappings file"); + + return 0; + } + + private void iterateFolder(File file, SkidTimedLogger logger, AtomicReference ghostLibrary) { + if (file.isDirectory()) { + for (File files : file.listFiles()) { + iterateFolder(files, logger, ghostLibrary); + } + } else { + if (file.getAbsolutePath().endsWith(".jar") || file.getAbsolutePath().endsWith(".jmod")) { + final GhostLibrary ghost = GhostHelper.createFromLibraryFile(logger, file); + logger.style("Creating mappings for " + file.getAbsolutePath() + "...\n"); + if (ghostLibrary.get() == null) { + ghostLibrary.set(ghost); + } else { + ghostLibrary.get().merge(ghost); + } + } + } + } +} diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/command/ObfuscateCommand.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/command/ObfuscateCommand.java index 50ec32a..142c6b6 100644 --- a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/command/ObfuscateCommand.java +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/command/ObfuscateCommand.java @@ -4,6 +4,7 @@ import dev.skidfuscator.obfuscator.Skidfuscator; import dev.skidfuscator.obfuscator.SkidfuscatorSession; import dev.skidfuscator.obfuscator.util.ConsoleColors; +import dev.skidfuscator.obfuscator.util.LogoUtil; import dev.skidfuscator.obfuscator.util.MiscUtil; import picocli.CommandLine; @@ -89,66 +90,6 @@ public class ObfuscateCommand implements Callable { @Override public Integer call() { - /* Total number of processors or cores available to the JVM */ - final String processors = - String.format("%19.19s", "Processors:") - + " " - + String.format( - "%-19.19s", - Runtime.getRuntime().availableProcessors() + " cores" - ); - - final long freeMemory = Math.round(Runtime.getRuntime().freeMemory() / 1E6); - final String memory = - String.format("%19.19s", "Current Memory:") - + " " - + String.format("%-19.19s", freeMemory + "mb"); - - final long maxMemory = Math.round(Runtime.getRuntime().maxMemory() / 1E6); - final String memoryString = (maxMemory == Long.MAX_VALUE - ? ConsoleColors.GREEN + "no limit" - : maxMemory + "mb" - ); - String topMemory = - String.format("%19.19s", "Max Memory:") - + " " - + String.format("%-19.19s", - memoryString + (maxMemory > 1500 ? "" : " ⚠️") - ); - - topMemory = MiscUtil.replaceColor( - topMemory, - memoryString, - maxMemory > 1500 ? ConsoleColors.GREEN_BRIGHT : ConsoleColors.RED_BRIGHT - ); - // slight fix for thing - topMemory = topMemory.replace("⚠️", "⚠️ "); - - final String[] logo = new String[] { - "", - " /$$$$$$ /$$ /$$ /$$ /$$$$$$ /$$", - " /$$__ $$| $$ |__/ | $$ /$$__ $$ | $$", - "| $$ \\__/| $$ /$$ /$$ /$$$$$$$| $$ \\__//$$ /$$ /$$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$", - "| $$$$$$ | $$ /$$/| $$ /$$__ $$| $$$$ | $$ | $$ /$$_____/ /$$_____/ |____ $$|_ $$_/ /$$__ $$ /$$__ $$", - " \\____ $$| $$$$$$/ | $$| $$ | $$| $$_/ | $$ | $$| $$$$$$ | $$ /$$$$$$$ | $$ | $$ \\ $$| $$ \\__/", - " /$$ \\ $$| $$_ $$ | $$| $$ | $$| $$ | $$ | $$ \\____ $$| $$ /$$__ $$ | $$ /$$| $$ | $$| $$", - "| $$$$$$/| $$ \\ $$| $$| $$$$$$$| $$ | $$$$$$/ /$$$$$$$/| $$$$$$$| $$$$$$$ | $$$$/| $$$$$$/| $$", - " \\______/ |__/ \\__/|__/ \\_______/|__/ \\______/ |_______/ \\_______/ \\_______/ \\___/ \\______/ |__/", - "", - " ┌───────────────────────────────────────────┐", - " │ " + processors + " │", - " │ " + memory + " │", - " │ " + topMemory + " │", - " └───────────────────────────────────────────┘", - "", - " Author: Ghast Version: 2.0.8 Today: " - + DateFormat.getDateTimeInstance().format(new Date(Instant.now().toEpochMilli())), - "" - }; - - for (String s : logo) { - System.out.println(s); - } if (input == null) { return -1; diff --git a/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/LogoUtil.java b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/LogoUtil.java new file mode 100644 index 0000000..3d7bbc6 --- /dev/null +++ b/dev.skidfuscator.client.standalone/src/main/java/dev/skidfuscator/obfuscator/util/LogoUtil.java @@ -0,0 +1,73 @@ +package dev.skidfuscator.obfuscator.util; + +import lombok.experimental.UtilityClass; + +import java.text.DateFormat; +import java.time.Instant; +import java.util.Date; + +@UtilityClass +public class LogoUtil { + public static void printLogo() { + /* Total number of processors or cores available to the JVM */ + final String processors = + String.format("%19.19s", "Processors:") + + " " + + String.format( + "%-19.19s", + Runtime.getRuntime().availableProcessors() + " cores" + ); + + final long freeMemory = Math.round(Runtime.getRuntime().freeMemory() / 1E6); + final String memory = + String.format("%19.19s", "Current Memory:") + + " " + + String.format("%-19.19s", freeMemory + "mb"); + + final long maxMemory = Math.round(Runtime.getRuntime().maxMemory() / 1E6); + final String memoryString = (maxMemory == Long.MAX_VALUE + ? ConsoleColors.GREEN + "no limit" + : maxMemory + "mb" + ); + String topMemory = + String.format("%19.19s", "Max Memory:") + + " " + + String.format("%-19.19s", + memoryString + (maxMemory > 1500 ? "" : " ⚠️") + ); + + topMemory = MiscUtil.replaceColor( + topMemory, + memoryString, + maxMemory > 1500 ? ConsoleColors.GREEN_BRIGHT : ConsoleColors.RED_BRIGHT + ); + // slight fix for thing + topMemory = topMemory.replace("⚠️", "⚠️ "); + + final String[] logo = new String[] { + "", + " /$$$$$$ /$$ /$$ /$$ /$$$$$$ /$$", + " /$$__ $$| $$ |__/ | $$ /$$__ $$ | $$", + "| $$ \\__/| $$ /$$ /$$ /$$$$$$$| $$ \\__//$$ /$$ /$$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$", + "| $$$$$$ | $$ /$$/| $$ /$$__ $$| $$$$ | $$ | $$ /$$_____/ /$$_____/ |____ $$|_ $$_/ /$$__ $$ /$$__ $$", + " \\____ $$| $$$$$$/ | $$| $$ | $$| $$_/ | $$ | $$| $$$$$$ | $$ /$$$$$$$ | $$ | $$ \\ $$| $$ \\__/", + " /$$ \\ $$| $$_ $$ | $$| $$ | $$| $$ | $$ | $$ \\____ $$| $$ /$$__ $$ | $$ /$$| $$ | $$| $$", + "| $$$$$$/| $$ \\ $$| $$| $$$$$$$| $$ | $$$$$$/ /$$$$$$$/| $$$$$$$| $$$$$$$ | $$$$/| $$$$$$/| $$", + " \\______/ |__/ \\__/|__/ \\_______/|__/ \\______/ |_______/ \\_______/ \\_______/ \\___/ \\______/ |__/", + "", + " ┌───────────────────────────────────────────┐", + " │ " + processors + " │", + " │ " + memory + " │", + " │ " + topMemory + " │", + " └───────────────────────────────────────────┘", + "", + " Author: Ghast Version: 2.0.8 Today: " + + DateFormat.getDateTimeInstance().format(new Date(Instant.now().toEpochMilli())), + "" + }; + + for (String s : logo) { + System.out.println(s); + } + } +} diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java index 5d549dc..cc49ea6 100644 --- a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java @@ -8,6 +8,9 @@ import dev.skidfuscator.config.DefaultSkidConfig; import dev.skidfuscator.obfuscator.creator.SkidApplicationClassSource; import dev.skidfuscator.obfuscator.creator.SkidCache; +import dev.skidfuscator.obfuscator.dependency.CommonDependency; +import dev.skidfuscator.obfuscator.dependency.DependencyDownloader; +import dev.skidfuscator.obfuscator.dependency.matcher.DependencyMatcher; import dev.skidfuscator.obfuscator.directory.SkiddedDirectory; import dev.skidfuscator.obfuscator.event.EventBus; import dev.skidfuscator.obfuscator.event.impl.transform.ClassTransformEvent; @@ -113,6 +116,7 @@ public class Skidfuscator { private PredicateAnalysis predicateAnalysis; private final SkidRemapper classRemapper = new SkidRemapper(new HashMap<>()); + private final DependencyDownloader dependencyDownloader = new DependencyDownloader(); private final Counter counter = new Counter(); @@ -685,28 +689,78 @@ private void _verify() { LOGGER.post("Starting verification"); try { classSource.getClassTree().verify(); - } catch (Exception e) { - LOGGER.error("\n" + - "-----------------------------------------------------\n" - + "/!\\ Skidfuscator failed to compute some libraries!\n" - + "It it advised to read https://github.com/terminalsin/skidfuscator-java-obfuscator/wiki/Libraries\n" - + "\n" - + "The following class was NOT found. This can be a dependency of a dependency." - + "Error: " + e.getMessage() + "\n" + - (e.getCause() == null - ? "\n" - : " " + e.getCause().getMessage() + "\n" - ) - + "-----------------------------------------------------\n" - , e); - - if (!CLOUD) - System.exit(1); + } catch (Exception ex) { + final List missingClasses = classSource.getClassTree().getMissingClasses(); + + LOGGER.warn("Attempting to auto-resolve missing classes..."); + final Set commonDependencies = Arrays.stream(CommonDependency.values()).filter(f -> f.getMatcher().test(missingClasses)).collect(Collectors.toSet()); + + if (commonDependencies.isEmpty()) { + LOGGER.warn("\n" + + "-----------------------------------------------------\n" + + "/!\\ Skidfuscator failed to compute some libraries!\n" + + "PLEASE READ THE FOLLOWING WITH MUCH ATTENTION\n" + + "-----------------------------------------------------\n" + + "It it advised to read https://skidfuscator.dev/docs/libraries.html\n" + + "\n" + + "The following classes were NOT found. This means they are \n" + + "either not present in the libraries or the libraries are \n" + + "corrupted. Libraries themselves can have dependencies\n" + + "\n" + + "List of missing classes:\n" + + missingClasses.stream().map(f -> " --> " + f + "\n").collect(Collectors.joining()) + + "-----------------------------------------------------\n" + ); + + if (!CLOUD) + System.exit(1); + return; + } + commonDependencies.forEach(e -> { + LOGGER.warn("Found common dependency: " + e.name() + "...\n"); + dependencyDownloader.download(e); + LOGGER.warn("Downloaded " + e.name() + "...\n"); + }); + + + final Path mappingsDir = Paths.get("mappings"); + this.importMappingFolder(mappingsDir.toFile()); + + LOGGER.warn(String.format( + "Resolved %d common dependencies... retrying verification...\n", + commonDependencies.size() + )); + _verify(); return; } LOGGER.log("Finished verification!"); } + private void importMappingFolder(final File folder) { + for (File lib : folder.listFiles()) { + if (lib.isDirectory()) { + importMappingFolder(lib); + continue; + } + + final String absolute = lib.getAbsolutePath(); + if (!absolute.endsWith(".json")) { + LOGGER.debug(String.format("Skipping over %s since not end in json", absolute)); + continue; + } + + final GhostLibrary library = GhostHelper.readFromLibraryFile(LOGGER, lib); + final ApplicationClassSource libraryClassSource = GhostHelper.importFile(LOGGER, session.isFuckIt(), library); + /* Add library source to class source */ + classSource.addLibraries(new LibraryClassSource( + libraryClassSource, + 5 + )); + + LOGGER.style(String.format("Importing %s... please wait...\n", absolute)); + } + } + protected void _cleanup() { this.hierarchy = null; this.irFactory.clear(); diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/CommonDependency.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/CommonDependency.java new file mode 100644 index 0000000..026f2ac --- /dev/null +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/CommonDependency.java @@ -0,0 +1,30 @@ +package dev.skidfuscator.obfuscator.dependency; + +import dev.skidfuscator.obfuscator.dependency.matcher.DependencyMatcher; + +import java.util.List; + +public enum CommonDependency { + BUKKIT("https://github.com/skidfuscatordev/mappings/raw/refs/heads/main/spigot/1.21/paper-1.21.zip", new DependencyMatcher() { + @Override + public boolean test(List strings) { + return strings.stream().anyMatch(e -> e.contains("org/bukkit")); + } + }); + + private final String url; + private final DependencyMatcher matcher; + + CommonDependency(String url, DependencyMatcher matcher) { + this.url = url; + this.matcher = matcher; + } + + public String getUrl() { + return url; + } + + public DependencyMatcher getMatcher() { + return matcher; + } +} diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/DependencyDownloader.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/DependencyDownloader.java new file mode 100644 index 0000000..1b731e8 --- /dev/null +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/DependencyDownloader.java @@ -0,0 +1,73 @@ +package dev.skidfuscator.obfuscator.dependency; + +import dev.skidfuscator.obfuscator.Skidfuscator; +import lombok.SneakyThrows; + +import java.io.BufferedInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class DependencyDownloader { + + @SneakyThrows + public void download(final CommonDependency dependency) { + final String url = dependency.getUrl(); + + // Resolve path + Path mappingsDir = Paths.get("mappings"); + Files.createDirectories(mappingsDir); + + Path zipFilePath = mappingsDir.resolve(dependency.name().toLowerCase() + ".zip"); + + // Download the zip file + Skidfuscator.LOGGER.style(String.format("Downloading dependency %s from %s\n", dependency.name(), url)); + try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); + FileOutputStream fileOutputStream = new FileOutputStream(zipFilePath.toFile())) { + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + fileOutputStream.write(dataBuffer, 0, bytesRead); + } + } + + if (!Files.exists(zipFilePath)) { + throw new IOException("Failed to download the file: " + zipFilePath); + } + + Skidfuscator.LOGGER.style(String.format("Downloaded dependency %s to %s\n", dependency.name(), zipFilePath)); + + if (url.endsWith(".jar")) { + throw new IllegalArgumentException("Invalid dependency type"); + } else if (url.endsWith(".zip")) { + try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipFilePath))) { + ZipEntry entry; + while ((entry = zipInputStream.getNextEntry()) != null) { + Path filePath = mappingsDir.resolve(entry.getName()); + if (entry.isDirectory()) { + Files.createDirectories(filePath); + } else { + Files.createDirectories(filePath.getParent()); + try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) { + byte[] buffer = new byte[1024]; + int len; + while ((len = zipInputStream.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + } + } + zipInputStream.closeEntry(); + } + } + Skidfuscator.LOGGER.style(String.format("Extracted dependency %s to %s\n", dependency.name(), mappingsDir.toFile().getAbsolutePath())); + Files.delete(zipFilePath); + } else { + Skidfuscator.LOGGER.style(String.format("Unsupported file type for %s\n", url)); + } + } +} diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/matcher/DependencyMatcher.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/matcher/DependencyMatcher.java new file mode 100644 index 0000000..3933d97 --- /dev/null +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/dependency/matcher/DependencyMatcher.java @@ -0,0 +1,8 @@ +package dev.skidfuscator.obfuscator.dependency.matcher; + +import java.util.List; +import java.util.function.Predicate; + +public interface DependencyMatcher extends Predicate> { + +} diff --git a/org.mapleir.parent/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java b/org.mapleir.parent/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java index c0e5f2d..deef403 100644 --- a/org.mapleir.parent/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java +++ b/org.mapleir.parent/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java @@ -57,15 +57,25 @@ public List getMissingClasses() { List missing = new ArrayList<>(); for (ClassNode cn : source.iterate()) { if(cn != rootNode) { - ClassNode sup = cn.node.superName != null - ? requestClass0(cn.node.superName, cn.getName()) - : rootNode; + ClassNode sup = null; + try { + sup = cn.node.superName != null + ? requestClass0(cn.node.superName, cn.getName()) + : rootNode; + } catch (RuntimeException e) { + // ignore + } if(sup == null) { missing.add(cn.node.superName); } for (String s : cn.node.interfaces) { - ClassNode iface = requestClass0(s, cn.getName()); + ClassNode iface = null; + try { + iface = requestClass0(s, cn.getName()); + } catch (RuntimeException e) { + // ignore + } if(iface == null) { missing.add(s); }