From 63e94212fde867e11eeae418f88c2eeed7487097 Mon Sep 17 00:00:00 2001 From: Shanyu Juneja <30368557+terminalsin@users.noreply.github.com> Date: Mon, 10 Jan 2022 20:28:55 +0100 Subject: [PATCH] CLI implementation (#5) * Added CLI run * Fixed odd manifest issue * Added version and parenting command * Prettified everything, ready for partial release --- .../org/mapleir/app/service/ClassTree.java | 104 +++++-- .../service/CompleteResolvingJarDumper.java | 10 +- org.mapleir.main/pom.xml | 41 ++- .../src/main/java/org/mapleir/Main.java | 32 ++- .../src/main/java/org/mapleir/cli/CliLog.java | 63 +++++ .../java/org/mapleir/cli/cmd/MainCommand.java | 27 ++ .../java/org/mapleir/cli/cmd/RunCommand.java | 263 ++++++++++++++++++ .../org/mapleir/cli/cmd/VersionCommand.java | 26 ++ .../deob/passes/fixer/ExceptionFixerPass.java | 49 ++-- .../java/org/mapleir/test/ClassTreeTest.java | 4 + 10 files changed, 553 insertions(+), 66 deletions(-) create mode 100644 org.mapleir.main/src/main/java/org/mapleir/cli/CliLog.java create mode 100644 org.mapleir.main/src/main/java/org/mapleir/cli/cmd/MainCommand.java create mode 100644 org.mapleir.main/src/main/java/org/mapleir/cli/cmd/RunCommand.java create mode 100644 org.mapleir.main/src/main/java/org/mapleir/cli/cmd/VersionCommand.java create mode 100644 org.mapleir.main/src/test/java/org/mapleir/test/ClassTreeTest.java diff --git a/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java b/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java index 8fe1ea1f..300be0f5 100644 --- a/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java +++ b/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java @@ -1,10 +1,13 @@ package org.mapleir.app.service; +import java.io.IOException; +import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.atomic.AtomicReference; import org.apache.log4j.Logger; import org.mapleir.app.service.ClassTree.InheritanceEdge; +import org.mapleir.asm.ClassHelper; import org.mapleir.stdlib.collections.graph.FastDirectedGraph; import org.mapleir.stdlib.collections.graph.FastGraphEdge; import org.mapleir.stdlib.collections.graph.FastGraphEdgeImpl; @@ -24,42 +27,42 @@ public class ClassTree extends FastDirectedGraph { private static final Logger LOGGER = Logger.getLogger(ClassTree.class); private static final boolean ALLOW_PHANTOM_CLASSES = true; - + private final ApplicationClassSource source; private final ClassNode rootNode; private final boolean allowPhantomClasses; - + public ClassTree(ApplicationClassSource source) { this(source, ALLOW_PHANTOM_CLASSES); } - + public ClassTree(ApplicationClassSource source, boolean allowPhantomClasses) { this.source = source; this.allowPhantomClasses = allowPhantomClasses; rootNode = findClass("java/lang/Object"); addVertex(rootNode); } - + protected void init() { for (ClassNode node : source.iterateWithLibraries()) { addVertex(node); } } - + public ClassNode getRootNode() { return rootNode; } - + public Iterable iterateParents(ClassNode cn) { // this avoids any stupid anonymous Iterable and Iterator bullcrap // and also avoids computing a temporary set, so it is performant return () -> getEdges(cn).stream().map(e -> e.dst()).iterator(); } - + public Iterable iterateInterfaces(ClassNode cn) { return () -> getEdges(cn).stream().filter(e -> e instanceof ImplementsEdge).map(e -> e.dst()).iterator(); } - + public Iterable iterateChildren(ClassNode cn) { return () -> getReverseEdges(cn).stream().map(e -> e.src()).iterator(); } @@ -79,15 +82,15 @@ public ClassNode next() { } }; } - + public Collection getParents(ClassNode cn) { return __getnodes(getEdges(cn), true); } - + public Collection getChildren(ClassNode cn) { return __getnodes(getReverseEdges(cn), false); } - + private Collection __getnodes(Collection> edges, boolean dst) { Set set = new HashSet<>(); for(FastGraphEdge e : edges) { @@ -111,7 +114,7 @@ public List getAllChildren(ClassNode cn) { } return SimpleDfs.postorder(this, cn, true); } - + /** * @param cn classnode to search out from * @return every class connected to the class in any way. @@ -135,7 +138,7 @@ public Collection getAllBranches(ClassNode cn) { } return results; } - + public ClassNode getSuper(ClassNode cn) { if (cn == rootNode) return null; @@ -144,7 +147,7 @@ public ClassNode getSuper(ClassNode cn) { return edge.dst(); throw new IllegalStateException("Couldn't find parent class?"); } - + protected ClassNode findClass(String name) { LocateableClassNode n = source.findClass(name); if(n != null) { @@ -158,7 +161,7 @@ protected ClassNode findClass(String name) { } } } - + private ClassNode requestClass0(String name, String from) { try { return findClass(name); @@ -166,17 +169,68 @@ private ClassNode requestClass0(String name, String from) { throw new RuntimeException("request from " + from, e); } } - + + public ClassNode getCommonSuperType(String type1, String type2) { + ClassNode ccn = source.findClassNode(type1); + ClassNode dcn = source.findClassNode(type2); + + if(ccn == null) { + ClassNode c; + try { + c = ClassHelper.create(type1); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + this.addVertex(ccn = c); + } + + if(dcn == null) { + ClassNode c; + try { + c = ClassHelper.create(type2); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + this.addVertex(dcn = c); + } + + Collection c = this.getAllParents(ccn); + Collection d = this.getAllParents(dcn); + + if(c.contains(dcn)) + return dcn; + + if(d.contains(ccn)) + return ccn; + + Stack stack = new Stack<>(); + List cached = new ArrayList<>(c); + Collections.reverse(cached); + stack.addAll(cached); + + while (!stack.isEmpty()) { + final ClassNode peek = stack.pop(); + + if (d.contains(peek)) + return peek; + } + + return null; + } + @Override public boolean addVertex(ClassNode cn) { if(cn == null) { LOGGER.error("Received null to ClassTree.addVertex"); return false; } - + if (!super.addVertex(cn)) return false; - + if(cn != rootNode) { Set edges = new HashSet<>(); ClassNode sup = cn.node.superName != null ? requestClass0(cn.node.superName, cn.getName()) : rootNode; @@ -196,12 +250,12 @@ public boolean addVertex(ClassNode cn) { } edges.add(new ImplementsEdge(cn, iface)); } - + for(InheritanceEdge e : edges) { super.addEdge(e); } } - + return true; } @@ -232,7 +286,7 @@ public Set getReverseEdges(ClassNode cn) { } return super.getReverseEdges(cn); } - + @Override public String toString() { TabbedStringWriter sw = new TabbedStringWriter(); @@ -241,7 +295,7 @@ public String toString() { } return sw.toString(); } - + public static void blockToString(TabbedStringWriter sw, ClassTree ct, ClassNode cn) { sw.print(String.format("%s", cn.getDisplayName())); sw.tab(); @@ -254,7 +308,7 @@ public static void blockToString(TabbedStringWriter sw, ClassTree ct, ClassNode sw.untab(); sw.print("\n"); } - + public interface InheritanceEdge extends FastGraphEdge { } @@ -262,7 +316,7 @@ public static class ExtendsEdge extends FastGraphEdgeImpl implements public ExtendsEdge(ClassNode child, ClassNode parent) { super(child, parent); } - + @Override public String toString() { return String.format("#%s extends #%s", src.getDisplayName(), dst.getDisplayName()); @@ -273,7 +327,7 @@ public static class ImplementsEdge extends FastGraphEdgeImpl implemen public ImplementsEdge(ClassNode child, ClassNode parent) { super(child, parent); } - + @Override public String toString() { return String.format("#%s implements #%s", src.getDisplayName(), dst.getDisplayName()); diff --git a/org.mapleir.app-services/src/main/java/org/mapleir/app/service/CompleteResolvingJarDumper.java b/org.mapleir.app-services/src/main/java/org/mapleir/app/service/CompleteResolvingJarDumper.java index 68d0c316..2313ea3f 100644 --- a/org.mapleir.app-services/src/main/java/org/mapleir/app/service/CompleteResolvingJarDumper.java +++ b/org.mapleir.app-services/src/main/java/org/mapleir/app/service/CompleteResolvingJarDumper.java @@ -124,12 +124,14 @@ protected String getCommonSuperClass(String type1, String type2) { try { c = ClassHelper.create(type1); } catch (IOException e) { + e.printStackTrace(); return "java/lang/Object"; } if(c == null) { return "java/lang/Object"; } - throw new UnsupportedOperationException(c.toString()); + + tree.addVertex(ccn = c); // classTree.build(c); // return getCommonSuperClass(type1, type2); } @@ -141,13 +143,15 @@ protected String getCommonSuperClass(String type1, String type2) { try { c = ClassHelper.create(type2); } catch (IOException e) { + e.printStackTrace(); return "java/lang/Object"; } if(c == null) { return "java/lang/Object"; } - throw new UnsupportedOperationException(c.toString()); - // classTree.build(c); + + tree.addVertex(dcn = c); + // classTree.build(c) // return getCommonSuperClass(type1, type2); } diff --git a/org.mapleir.main/pom.xml b/org.mapleir.main/pom.xml index 31dc4e7b..3ff9e8a2 100644 --- a/org.mapleir.main/pom.xml +++ b/org.mapleir.main/pom.xml @@ -3,11 +3,12 @@ org.mapleir parent - 0.0.1-ALPHA + 1.0.0-SNAPSHOT ../org.mapleir.parent org.mapleir main + MapleIR-main com.google.guava @@ -19,25 +20,30 @@ junit 4.12 - + + info.picocli + picocli + 4.6.2 + + org.mapleir modasm - 0.0.1-ALPHA + 1.0.0-SNAPSHOT org.mapleir ir - 0.0.1-ALPHA + 1.0.0-SNAPSHOT org.mapleir app-services - 0.0.1-ALPHA + 1.0.0-SNAPSHOT org.mapleir topdank-services - 0.0.1-ALPHA + 1.0.0-SNAPSHOT org.mapleir @@ -52,7 +58,7 @@ org.mapleir ir.printer - 0.0.1-ALPHA + 1.0.0-SNAPSHOT @@ -69,6 +75,11 @@ shade + + + org.mapleir.Main + + org.mapleir @@ -89,6 +100,15 @@ jar-with-dependencies + + + org.mapleir.Main + + + org.mapleir.Main + maple-ir + + @@ -100,15 +120,18 @@ - org.apache.maven.plugins maven-jar-plugin 3.0.2 + + org.mapleir.Main + - maple-ir + org.mapleir.Main + maple-ir diff --git a/org.mapleir.main/src/main/java/org/mapleir/Main.java b/org.mapleir.main/src/main/java/org/mapleir/Main.java index 92c2d48b..6008485b 100644 --- a/org.mapleir.main/src/main/java/org/mapleir/Main.java +++ b/org.mapleir.main/src/main/java/org/mapleir/Main.java @@ -1,8 +1,30 @@ package org.mapleir; -/** - * @author Ghast - * @since 27/11/2020 - * mapleir © 2020 - */ + +import org.mapleir.cli.cmd.MainCommand; +import org.mapleir.cli.cmd.RunCommand; +import org.mapleir.cli.cmd.VersionCommand; +import picocli.CommandLine; + public class Main { + public static final String VERSION = Main.class.getPackage().getImplementationVersion(); + public static final String LOGO = "" + + " __ ___ __ ________ \n" + + " / |/ /___ _____ / /__ / _/ __ \\\n" + + " / /|_/ / __ `/ __ \\/ / _ \\______ / // /_/ /\n" + + " / / / / /_/ / /_/ / / __/_____// // _, _/ \n" + + "/_/ /_/\\__,_/ .___/_/\\___/ /___/_/ |_| \n" + + " /_/ \n"; + + public static void main(final String[] args) { + System.out.println( + Main.LOGO + + "Maple-IR | Copyright (c) 2022 Bibl and rcx \n" + + ); + final int exitCode = new CommandLine(new MainCommand()) + .addSubcommand("run", new RunCommand()) + .addSubcommand("version", new VersionCommand()) + .execute(args); + System.exit(exitCode); + } } diff --git a/org.mapleir.main/src/main/java/org/mapleir/cli/CliLog.java b/org.mapleir.main/src/main/java/org/mapleir/cli/CliLog.java new file mode 100644 index 00000000..a6ff0540 --- /dev/null +++ b/org.mapleir.main/src/main/java/org/mapleir/cli/CliLog.java @@ -0,0 +1,63 @@ +package org.mapleir.cli; + +import org.apache.log4j.Logger; +import org.mapleir.Boot; + +import java.util.Deque; +import java.util.LinkedList; + +public class CliLog { + private final Logger LOGGER = Logger.getLogger(Boot.class); + private long timer; + private final Deque sections = new LinkedList<>(); + + private double lap() { + long now = System.nanoTime(); + long delta = now - timer; + timer = now; + return (double)delta / 1_000_000_000L; + } + + private void section0(String endText, String sectionText, boolean quiet) { + if(sections.isEmpty()) { + lap(); + if(!quiet) + LOGGER.info(sectionText); + } else { + /* remove last section. */ + sections.pop(); + if(!quiet) { + LOGGER.info(String.format(endText, lap())); + LOGGER.info(sectionText); + } else { + lap(); + } + } + + /* push the new one. */ + sections.push(sectionText); + } + + public void section0(String endText, String sectionText) { + if(sections.isEmpty()) { + lap(); + LOGGER.info(sectionText); + } else { + /* remove last section. */ + sections.pop(); + LOGGER.info(String.format(endText, lap())); + LOGGER.info(sectionText); + } + + /* push the new one. */ + sections.push(sectionText); + } + + public void section(String text) { + section0("...took %fs.", text); + } + + public void print(String text) { + LOGGER.info(text); + } +} diff --git a/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/MainCommand.java b/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/MainCommand.java new file mode 100644 index 00000000..e14ef02f --- /dev/null +++ b/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/MainCommand.java @@ -0,0 +1,27 @@ +package org.mapleir.cli.cmd; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.mapleir.Main; +import picocli.CommandLine; + +import java.util.concurrent.Callable; + +@CommandLine.Command( + name = "main", + mixinStandardHelpOptions = true, + version = "main 1.0", + description = "Main parent command." +) +public class MainCommand implements Callable { + private static final Logger LOGGER = LogManager.getLogger(Main.class); + + @Override + public Integer call() throws Exception { + System.out.println( + "java -jar maple.jar run | Run deobfuscation\n" + + "java -jar maple.jar version | View version\n" + ); + return 0; + } +} diff --git a/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/RunCommand.java b/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/RunCommand.java new file mode 100644 index 00000000..0b012a8e --- /dev/null +++ b/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/RunCommand.java @@ -0,0 +1,263 @@ +package org.mapleir.cli.cmd; + +import org.apache.log4j.Logger; +import org.mapleir.Boot; +import org.mapleir.DefaultInvocationResolver; +import org.mapleir.Main; +import org.mapleir.app.client.SimpleApplicationContext; +import org.mapleir.app.service.ApplicationClassSource; +import org.mapleir.app.service.CompleteResolvingJarDumper; +import org.mapleir.app.service.LibraryClassSource; +import org.mapleir.asm.ClassNode; +import org.mapleir.asm.MethodNode; +import org.mapleir.cli.CliLog; +import org.mapleir.context.AnalysisContext; +import org.mapleir.context.BasicAnalysisContext; +import org.mapleir.context.IRCache; +import org.mapleir.deob.IPass; +import org.mapleir.deob.PassContext; +import org.mapleir.deob.PassGroup; +import org.mapleir.deob.PassResult; +import org.mapleir.deob.dataflow.LiveDataFlowAnalysisImpl; +import org.mapleir.deob.passes.fixer.ExceptionFixerPass; +import org.mapleir.deob.passes.rename.ClassRenamerPass; +import org.mapleir.deob.util.RenamingHeuristic; +import org.mapleir.ir.algorithms.BoissinotDestructor; +import org.mapleir.ir.algorithms.LocalsReallocator; +import org.mapleir.ir.cfg.ControlFlowGraph; +import org.mapleir.ir.cfg.builder.ControlFlowGraphBuilder; +import org.mapleir.ir.codegen.ControlFlowGraphDumper; +import org.topdank.byteengineer.commons.data.JarInfo; +import org.topdank.byteio.in.SingleJarDownloader; +import picocli.CommandLine; + +import java.io.*; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.jar.JarOutputStream; + +@CommandLine.Command( + name = "run", + mixinStandardHelpOptions = true, + version = "run 1.0", + description = "Compiles back and forth from SSA to optimize code." +) +public class RunCommand implements Callable { + @CommandLine.Parameters( + index = "0", + description = "The file which will be optimized." + ) + private File input; + + @CommandLine.Option( + names = {"-rt", "--runtime"}, + description = "Path to the runtime jar" + ) + private File runtime; + + @CommandLine.Option( + names = {"-o", "--output"}, + description = "Path to the output jar location" + ) + private File output; + + private final CliLog logger = new CliLog(); + + @Override + public Integer call() throws Exception { + + + if (input == null) { + logger.print("Fatal! Failed to find input jar!"); + return 1; + } + + // Initialization + logger.section("Preparing to run on " + input.getAbsolutePath()); + SingleJarDownloader dl = new SingleJarDownloader<>(new JarInfo(input)); + dl.download(); + String appName = input.getName().substring(0, input.getName().length() - 4); + ApplicationClassSource app = new ApplicationClassSource(appName, dl.getJarContents().getClassContents()); + + if (output == null) { + output = new File(appName + "-out.jar"); + } + + logger.section("Importing runtime..."); + if (runtime == null) { + runtime = new File(System.getProperty("java.home"), "lib/rt.jar"); + } + app.addLibraries(rt(app, runtime)); + + + logger.section("Initialising context."); + IRCache irFactory = new IRCache(ControlFlowGraphBuilder::build); + AnalysisContext cxt = new BasicAnalysisContext.BasicContextBuilder() + .setApplication(app) + .setInvocationResolver(new DefaultInvocationResolver(app)) + .setCache(irFactory) + .setApplicationContext(new SimpleApplicationContext(app)) + .setDataFlowAnalysis(new LiveDataFlowAnalysisImpl(irFactory)) + .build(); + + logger.section("Expanding callgraph and generating cfgs."); + for (ClassNode cn : cxt.getApplication().iterate()) { + for (MethodNode m : cn.getMethods()) { + cxt.getIRCache().getFor(m); + } + } + logger.section0("...generated " + cxt.getIRCache().size() + " cfgs in %fs.%n", "Preparing to transform."); + + // do passes + PassGroup masterGroup = new PassGroup("MasterController"); + for (IPass p : getTransformationPasses()) { + masterGroup.add(p); + } + run(cxt, masterGroup); + logger.section0("...done transforming in %fs.%n", "Preparing to transform."); + + + for(Map.Entry e : cxt.getIRCache().entrySet()) { + MethodNode mn = e.getKey(); + ControlFlowGraph cfg = e.getValue(); + cfg.verify(); + } + + logger.section("Retranslating SSA IR to standard flavour."); + for(Map.Entry e : cxt.getIRCache().entrySet()) { + MethodNode mn = e.getKey(); + // if (!mn.getName().equals("openFiles")) + // continue; + ControlFlowGraph cfg = e.getValue(); + + // System.out.println(cfg); + // CFGUtils.easyDumpCFG(cfg, "pre-destruct"); + cfg.verify(); + + BoissinotDestructor.leaveSSA(cfg); + + // CFGUtils.easyDumpCFG(cfg, "pre-reaalloc"); + LocalsReallocator.realloc(cfg); + // CFGUtils.easyDumpCFG(cfg, "post-reaalloc"); + // System.out.println(cfg); + cfg.verify(); + // System.out.println("Rewriting " + mn.getName()); + (new ControlFlowGraphDumper(cfg, mn)).dump(); + // System.out.println(InsnListUtils.insnListToString(mn.instructions)); + } + + logger.section("Rewriting jar."); + dumpJar(app, dl, masterGroup, output.getPath()); + + logger.section("Finished."); + + return 0; + } + + private LibraryClassSource rt(ApplicationClassSource app, File rtjar) throws IOException { + logger.section("Loading " + rtjar.getName() + " from " + rtjar.getAbsolutePath()); + SingleJarDownloader dl = new SingleJarDownloader<>(new JarInfo(rtjar)); + dl.download(); + + return new LibraryClassSource(app, dl.getJarContents().getClassContents()); + } + + private void dumpJar(ApplicationClassSource app, SingleJarDownloader dl, PassGroup masterGroup, String outputFile) throws IOException { + (new CompleteResolvingJarDumper(dl.getJarContents(), app) { + @Override + public int dumpResource(JarOutputStream out, String name, byte[] file) throws IOException { +// if(name.startsWith("META-INF")) { +// System.out.println(" ignore " + name); +// return 0; +// } + if(name.equals("META-INF/MANIFEST.MF")) { + ClassRenamerPass renamer = (ClassRenamerPass) masterGroup.getPass(e -> e.is(ClassRenamerPass.class)); + + if(renamer != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(baos)); + BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(file))); + + String line; + while((line = br.readLine()) != null) { + String[] parts = line.split(": ", 2); + if(parts.length != 2) { + bw.write(line); + continue; + } + + if(parts[0].equals("Main-Class")) { + String newMain = renamer.getRemappedName(parts[1].replace(".", "/")).replace("/", "."); + logger.print(String.format("%s -> %s%n", parts[1], newMain)); + parts[1] = newMain; + } + + bw.write(parts[0]); + bw.write(": "); + bw.write(parts[1]); + bw.write(System.lineSeparator()); + } + + br.close(); + bw.close(); + + file = baos.toByteArray(); + } + } + return super.dumpResource(out, name, file); + } + }).dump(new File(outputFile)); + } + + private static void run(AnalysisContext cxt, PassGroup group) { + PassContext pcxt = new PassContext(cxt, null, new ArrayList<>()); + PassResult result = group.accept(pcxt); + + if(result.getError() != null) { + throw new RuntimeException(result.getError()); + } + } + + private static IPass[] getTransformationPasses() { + RenamingHeuristic heuristic = RenamingHeuristic.RENAME_ALL; + return new IPass[] { + new ExceptionFixerPass() +// new ConcreteStaticInvocationPass(), +// new ClassRenamerPass(heuristic), +// new MethodRenamerPass(heuristic), +// new FieldRenamerPass(), +// new CallgraphPruningPass(), + + // new PassGroup("Interprocedural Optimisations") + // .add(new ConstantParameterPass()) + // new LiftConstructorCallsPass(), +// new DemoteRangesPass(), + + // new ConstantExpressionReorderPass(), + // new FieldRSADecryptionPass(), + // new ConstantParameterPass(), +// new ConstantExpressionEvaluatorPass(), +// new DeadCodeEliminationPass() + + }; + } + + static File locateRevFile(int rev) { + return new File("res/gamepack" + rev + ".jar"); + } + + private static Set findEntries(ApplicationClassSource source) { + Set set = new HashSet<>(); + /* searches only app classes. */ + for(ClassNode cn : source.iterate()) { + for(MethodNode m : cn.getMethods()) { + if((m.getName().length() > 2 && !m.getName().equals("")) || m.node.instructions.size() == 0) { + set.add(m); + } + } + } + return set; + } + + +} diff --git a/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/VersionCommand.java b/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/VersionCommand.java new file mode 100644 index 00000000..4ad83fd8 --- /dev/null +++ b/org.mapleir.main/src/main/java/org/mapleir/cli/cmd/VersionCommand.java @@ -0,0 +1,26 @@ +package org.mapleir.cli.cmd; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.mapleir.Main; +import picocli.CommandLine; + +import java.util.concurrent.Callable; + +@CommandLine.Command( + name = "version", + mixinStandardHelpOptions = true, + version = "version 1.0", + description = "Version command." +) +public class VersionCommand implements Callable { + private static final Logger LOGGER = LogManager.getLogger(Main.class); + + @Override + public Integer call() throws Exception { + LOGGER.info( + "Running Maple-IR v" + Main.VERSION + "!" + ); + return 0; + } +} diff --git a/org.mapleir.main/src/main/java/org/mapleir/deob/passes/fixer/ExceptionFixerPass.java b/org.mapleir.main/src/main/java/org/mapleir/deob/passes/fixer/ExceptionFixerPass.java index 21284308..e7e4d12a 100644 --- a/org.mapleir.main/src/main/java/org/mapleir/deob/passes/fixer/ExceptionFixerPass.java +++ b/org.mapleir.main/src/main/java/org/mapleir/deob/passes/fixer/ExceptionFixerPass.java @@ -1,17 +1,19 @@ package org.mapleir.deob.passes.fixer; -import com.google.common.collect.Lists; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; +import org.mapleir.asm.ClassHelper; import org.mapleir.asm.ClassNode; import org.mapleir.deob.IPass; import org.mapleir.deob.PassContext; import org.mapleir.deob.PassResult; import org.mapleir.flowgraph.ExceptionRange; +import org.mapleir.ir.TypeUtils; import org.mapleir.ir.cfg.BasicBlock; import org.mapleir.ir.cfg.ControlFlowGraph; import org.objectweb.asm.Type; +import java.io.IOException; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -27,37 +29,36 @@ public PassResult accept(PassContext cxt) { if (range.getTypes().size() <= 1) continue; - final Stack stack = new Stack<>(); + ClassNode superType = null; for (Type type : range.getTypes()) { - final ClassNode classNode = cxt.getAnalysis().getApplication().findClassNode(type.getClassName()); + ClassNode classNode = cxt.getAnalysis().getApplication().findClassNode(type.getClassName()); - final List classNodeList = cxt.getAnalysis().getApplication().getClassTree().getAllParents(classNode); + if (classNode == null) { + try { + classNode = ClassHelper.create(type.getClassName()); + } catch (IOException e) { + continue; + } + } - if (stack.isEmpty()) { - stack.add(classNode); - stack.addAll(Lists.reverse(classNodeList)); + if (superType == null) { + superType = classNode; } else { - final Stack toIterate = new Stack<>(); - toIterate.add(classNode); - toIterate.addAll(Lists.reverse(classNodeList)); - - runner: { - while (!stack.isEmpty()) { - for (ClassNode node : toIterate) { - if (node.equals(stack.peek())) - break runner; - } - - stack.pop(); - } - - logger.fatal("[*] Could not find common exception type between " + Arrays.toString(range.getTypes().toArray())); - } + superType = cxt.getAnalysis() + .getApplication() + .getClassTree() + .getCommonSuperType(superType.getName(), classNode.getName()); } } + final Set types = new HashSet<>(Collections.singleton( + superType == null + ? TypeUtils.OBJECT_TYPE + : Type.getObjectType(superType.getName()) + )); + range.setTypes(types); + counter.incrementAndGet(); - range.setTypes(new HashSet<>(Collections.singleton(Type.getType(stack.peek().getName())))); } } diff --git a/org.mapleir.main/src/test/java/org/mapleir/test/ClassTreeTest.java b/org.mapleir.main/src/test/java/org/mapleir/test/ClassTreeTest.java new file mode 100644 index 00000000..ee8112d0 --- /dev/null +++ b/org.mapleir.main/src/test/java/org/mapleir/test/ClassTreeTest.java @@ -0,0 +1,4 @@ +package org.mapleir.test; + +public class ClassTreeTest { +}