diff --git a/docs/bug-naturalisation-pass.drawio.drawio b/docs/bug-naturalisation-pass.drawio.drawio new file mode 100644 index 00000000..9616469c --- /dev/null +++ b/docs/bug-naturalisation-pass.drawio.drawio @@ -0,0 +1 @@ +7VxbX9o8GP80XOqvRw6XoHXzHYob25yXgUboa2lYGibu0y+h6SFJS6u2Bd28wOZpmob/c36eaMc8W20/YLBeXiEX+h1Dc7cd87xjGAOrSz8Z4Ski6D2DUxbYczktJUy935ATNU7deC4MhYkEIZ94a5E4R0EA50SgAYzRozjtHvniW9dgARXCdA58lXrruWQZUftGL6V/hN5iGb9Z7w6iOysQT+bfJFwCFz1mSKbTMc8wQiS6Wm3PoM/Ai3GJnrsouJtsDMOAVHmgO5wNb39+/v7p5OrzZPP7vzXYjk/MaJVfwN/wLzxG9MuHfMvkKcaB7n7NLuebGf01elx6BE7XYM5oj5T1lLYkK5+OdHo5Q5vAhe54lhDA/GGBGXWyIb4XQE53AX6Y0GU8wgREO9VskWjsqGwm3yjEBG4LEdATXKlAQrSCBD/RKfwBMxYqLow9PnxMGavH3FpmmNrnNMBlaZGsnMJNLzjiz0C/r8AMXSp9fIgwWaIFCoDvpNQRjqBluNBROmeM0JqD+j8k5ImrEtgQJPIGbj3yI3N9t4PY5qPzLV95N3iKBwH9uj+yg7vsIH1oN2JPmcnwBmKPwgUxnzPf4F+7/Uf8D5fJIEKDQbCfwdQEALyApEyuVUHA0AfE+yWun8dW/ugN8uibEwHSe6IA6QNbXCJEGzyH/ClJOJJtvFxe9JcIjIB3bdKTSsxd5k6Z9EQPVZEfrV550BsSCEMUiMRttSUQqv0egdCbU9LIR/OHDtufaajW/NFb+YBZ4VFIMHpIfJtRj6WVYcmxtIaWY2kTYu2mVu8pSHlhMja0IYOKktkdbaQAhpdoNduE5Z6vEnjPEtF8SHUtx3l1cyC1m0LUVGXvGinAURiIiFAkcGfIR8wjBGgnhfee70sk4HuLgEUcFDbmPEYMVI8GJ0N+Y+W5rl8UjIiGrjGuDCSudFWu5DHFbIophlHFIOj7DEKWFS68BxuftGQmcsDLtRJ6YwFZnJxk4FM9buAOWWLBRNMHIQNXkG9R8gTvm+88tUM5z70SFHmuChNLfa9RoEIZNtt7DNdrXbQU9Ccuu8RFqwt187OHeJ0Ih8ZcvaGa2w+QZDzYPUaryJHJvksQyMb8VyQPhbpu2WW6nnCqHUtpKXhOCcIwg2jH6O5s3wzTqwW7SjHWaGCcFyjEeTKFC/g+9NECgxV9cp3JgoR7mfSojDf33hbGJZJmeVWgMmWJcnPMslUz/AYSn+ZtsFnVBlv5DK9sXF/HvsEL2HeQQkcFjukVeRNlHM2nnHKAX+CHaMwCnjLT1mxCWPweUxPfY/JaR5F7lOfH+0rlJtpBrU4xNjgZI34H1eLlG0pCSgJmKZgxcxLrVvONnNpCi5pdsy0+vGafmtmfnsBrW66NNBxwmmqA1LFHDsW0Y5+/XxWTMno7p87SrobZKht4XLrU47j0MvCIB9haUwojwB6K59B3ptPeL9f0Xu9UTDBO8oLWQaucU4sJ77i51rfy0+ODNddi83n8Qefb6651G3GA3b6Ud7bcXrNUU3vExVPbFNGqWj215B5VbRpn/auexiJUGkPGE0t1zSoINv9VT58lmmr777iqp5E8vJnqadc6pHN9Tt53jwJyAVaezwjfIXZBADiZv2fQir73K+q73X+lb32dCe8revL3VMXLdPDYquLWS8qqb7cqXl3XelV960F1zVZ17ciOpJQohFSJTo5XHuxIij1QEH2HR1JKuCIVsA5eIo5fdvyhQuOG6dW+vSC21sTCcezEuQRYVruxdtdQtDCOGVjUxYJtwGHq/tyw4++jOC5LCIK8xERW2jqJZZZZx8i6aWzVk3AnCYw6WG939HStKEy5gvRru3TCjCXzYaZCGm3r7ddIX2Iu5H5icrgqYy9aLZvaB7UXNTeLj9VeCCyXqmw1NZAN6bC8ae5vIMvz4wJ+YUVB2ztfbDirT0u1RlM2kg3XGm1dMZLvunPdk4PFQ4cltnqe7m9ob3Zl3T90f9POaTMr/c0bFJKTAJANpnCGLDn6S/uc9iDf6GXZl5iydsJ71ZCp/jst+nNYhc5Uhi2MfgMIhTrYUQzNzO2fiMAmwX69zrvqeZCiCKudEr3cB0rU97kl+p6yULXAoLa8QbXIt8Mv15fXH6K4/qPzxaG/vn5kn9+H429OVBfRbi/HY1YvGd8O76aMxm58cabOVzZ9EpVS6J60q+Enthpb45JNHI6mk/G3r874jg6+TZ2xM52eJq8YTyY3u9XOJlcOm+5cn7MZR2pdqhW62TDeRMcwLy5YwliPberr4oGlRMCz9SDLbNE26WpD8mwJgkW2nL2v46PXVFwrOdQnFZbNvD/tMpLzLe0gp1bS9ln1Nlq5NRrsoj/qaqmnKp/gHbzQYMsLmS03VW21qXqE6tWT1Su3b1OXetFh+i8dIqDTf4xhOn8A \ No newline at end of file diff --git a/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ApplicationClassSource.java b/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ApplicationClassSource.java index f6def313..babb6e86 100644 --- a/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ApplicationClassSource.java +++ b/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ApplicationClassSource.java @@ -57,10 +57,6 @@ public void addLibraries(LibraryClassSource... libs) { libraries.sort(Comparator.comparingInt(LibraryClassSource::getPriority)); Collections.reverse(libraries); - - for (LibraryClassSource library : libraries) { - System.out.println("LIBRARY --> " + library.parent.getName() + " [priority: " + library.getPriority() + "]"); - } } public ClassNode findClassNode(String name) { diff --git a/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java b/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java index 7832b00a..c7bc4420 100644 --- a/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java +++ b/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/ClassTree.java @@ -161,7 +161,7 @@ protected ClassNode findClass(String name) { private ClassNode requestClass0(String name, String from) { try { - return source.findClassNode(name); + return findClass(name); } catch(RuntimeException e) { throw new RuntimeException("request from " + from, e); } diff --git a/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/CompleteResolvingJarDumper.java b/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/CompleteResolvingJarDumper.java index fe68a3e6..2b3a2e70 100644 --- a/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/CompleteResolvingJarDumper.java +++ b/maple-ir/org.mapleir.app-services/src/main/java/org/mapleir/app/service/CompleteResolvingJarDumper.java @@ -4,6 +4,7 @@ import org.objectweb.asm.ClassWriter; import org.mapleir.asm.ClassNode; import org.mapleir.asm.MethodNode; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarContents; import org.topdank.byteengineer.commons.data.JarResource; import org.topdank.byteio.out.JarDumper; @@ -25,14 +26,14 @@ */ public class CompleteResolvingJarDumper implements JarDumper { - private final JarContents contents; + private final JarContents contents; private final ApplicationClassSource source; /** * Creates a new JarDumper. * * @param contents Contents of jar. */ - public CompleteResolvingJarDumper(JarContents contents, ApplicationClassSource source) { + public CompleteResolvingJarDumper(JarContents contents, ApplicationClassSource source) { this.contents = contents; this.source = source; } @@ -50,8 +51,8 @@ public void dump(File file) throws IOException { JarOutputStream jos = new JarOutputStream(new FileOutputStream(file)); int classesDumped = 0; int resourcesDumped = 0; - for (ClassNode cn : contents.getClassContents()) { - classesDumped += dumpClass(jos, cn.getName(), cn); + for (JarClassData cn : contents.getClassContents()) { + classesDumped += dumpClass(jos, cn); } for (JarResource res : contents.getResourceContents()) { resourcesDumped += dumpResource(jos, res.getName(), res.getData()); @@ -66,13 +67,13 @@ public void dump(File file) throws IOException { * Writes the {@link ClassNode} to the Jar. * * @param out The {@link JarOutputStream}. - * @param cn The ClassNode. - * @param name The entry name. + * @param classData The {@link JarClassData} * @throws IOException If there is a write error. * @return The amount of things dumped, 1 or if you're not dumping it 0. */ @Override - public int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOException { + public int dumpClass(JarOutputStream out, JarClassData classData) throws IOException { + ClassNode cn = classData.getClassNode(); JarEntry entry = new JarEntry(cn.getName() + ".class"); out.putNextEntry(entry); ClassTree tree = source.getClassTree(); diff --git a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/DefaultBlockFactory.java b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/DefaultBlockFactory.java index 949f7a6b..c44001b5 100644 --- a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/DefaultBlockFactory.java +++ b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/DefaultBlockFactory.java @@ -7,11 +7,14 @@ import org.mapleir.ir.cfg.builder.ssa.BlockBuilder; import org.mapleir.ir.cfg.builder.ssa.CfgBuilder; import org.mapleir.ir.cfg.builder.ssa.expr.*; +import org.mapleir.ir.cfg.builder.ssa.expr.invoke.StaticInvocationExprBuilder; import org.mapleir.ir.cfg.builder.ssa.stmt.*; import org.mapleir.ir.cfg.builder.ssa.stmt.copy.CopyPhiStmtBuilder; import org.mapleir.ir.cfg.builder.ssa.stmt.copy.CopyVarStmtBuilder; import org.mapleir.ir.code.Expr; import org.mapleir.ir.code.expr.*; +import org.mapleir.ir.code.expr.invoke.InvocationExpr; +import org.mapleir.ir.code.expr.invoke.StaticInvocationExpr; import org.mapleir.ir.code.stmt.*; import org.mapleir.ir.code.stmt.copy.CopyPhiStmt; import org.mapleir.ir.code.stmt.copy.CopyVarStmt; @@ -304,6 +307,63 @@ public ConstantExpr build() { }; } + @Override + public StaticInvocationExprBuilder static_invoke_expr() { + return new StaticInvocationExprBuilder() { + private InvocationExpr.CallType callType = InvocationExpr.CallType.STATIC; + private Expr[] args; + private String owner; + private String name; + private String desc; + + @Override + public StaticInvocationExprBuilder callType(InvocationExpr.CallType callType) { + this.callType = callType; + return this; + } + + @Override + public StaticInvocationExprBuilder args(Expr[] args) { + this.args = args; + return this; + } + + @Override + public StaticInvocationExprBuilder owner(String owner) { + this.owner = owner; + return this; + } + + @Override + public StaticInvocationExprBuilder name(String name) { + this.name = name; + return this; + } + + @Override + public StaticInvocationExprBuilder desc(String desc) { + this.desc = desc; + return this; + } + + @Override + public StaticInvocationExpr build() { + assert owner != null : "Owner name cannot be null"; + assert name != null : "Name cannot be null"; + assert desc != null : "Description cannot be null"; + + + return new StaticInvocationExpr( + callType, + args, + owner, + name, + desc + ); + } + }; + } + @Override public FieldLoadExprBuilder field_load_expr() { return new FieldLoadExprBuilder() { diff --git a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/SSAFactory.java b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/SSAFactory.java index 8e4f54e9..ddc19626 100644 --- a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/SSAFactory.java +++ b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/SSAFactory.java @@ -3,6 +3,7 @@ import org.mapleir.ir.cfg.builder.ssa.BlockBuilder; import org.mapleir.ir.cfg.builder.ssa.CfgBuilder; import org.mapleir.ir.cfg.builder.ssa.expr.*; +import org.mapleir.ir.cfg.builder.ssa.expr.invoke.StaticInvocationExprBuilder; import org.mapleir.ir.cfg.builder.ssa.stmt.*; import org.mapleir.ir.cfg.builder.ssa.stmt.copy.CopyPhiStmtBuilder; import org.mapleir.ir.cfg.builder.ssa.stmt.copy.CopyVarStmtBuilder; @@ -43,6 +44,8 @@ public interface SSAFactory { VarExprBuilder var_expr(); + StaticInvocationExprBuilder static_invoke_expr(); + CopyPhiStmtBuilder copy_phi_stmt(); CopyVarStmtBuilder copy_var_stmt(); diff --git a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/GenerationPass.java b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/GenerationPass.java index 51fb3f8a..09886f16 100644 --- a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/GenerationPass.java +++ b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/GenerationPass.java @@ -1249,7 +1249,13 @@ protected void _call(int op, String owner, String name, String desc) { callExpr = new VirtualInvocationExpr(VirtualInvocationExpr.resolveCallType(op), args, owner, name, desc); break; case Opcodes.INVOKESTATIC: - callExpr = new StaticInvocationExpr(args, owner, name, desc); + callExpr = builder.factory + .static_invoke_expr() + .args(args) + .owner(owner) + .name(name) + .desc(desc) + .build(); break; default: throw new IllegalArgumentException("invalid call opcode " + op); diff --git a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/NaturalisationPass.java b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/NaturalisationPass.java index 53a16edc..ad90d555 100644 --- a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/NaturalisationPass.java +++ b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/NaturalisationPass.java @@ -155,6 +155,19 @@ class MergePair { continue; } Set> inSuccs = in.cfg.getSuccessors(e -> !(e instanceof TryCatchEdge), in); + /* + * In a nutshell, to be able to properly merge two blocks, we're looking for this + * exact scenario: + * + * B.. B.. + * \ / <-- No matters. We don't really care + * BX1 (in) + * | <-- CRITICAL! Must only be ONE edge which is a successor and + * v only one reverse edge for the successor + * BX2 (b) + * / \ <-- We don't really care from here on out + * B.. B.; + */ if(inSuccs.size() != 1 || builder.graph.getReverseEdges(b).size() != 1) { continue; } diff --git a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/ssa/expr/invoke/StaticInvocationExprBuilder.java b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/ssa/expr/invoke/StaticInvocationExprBuilder.java new file mode 100644 index 00000000..0b4afc40 --- /dev/null +++ b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/cfg/builder/ssa/expr/invoke/StaticInvocationExprBuilder.java @@ -0,0 +1,14 @@ +package org.mapleir.ir.cfg.builder.ssa.expr.invoke; + +import org.mapleir.app.factory.Builder; +import org.mapleir.ir.code.Expr; +import org.mapleir.ir.code.expr.invoke.InvocationExpr; +import org.mapleir.ir.code.expr.invoke.StaticInvocationExpr; + +public interface StaticInvocationExprBuilder extends Builder { + StaticInvocationExprBuilder callType(InvocationExpr.CallType callType); + StaticInvocationExprBuilder args(Expr[] args); + StaticInvocationExprBuilder owner(String owner); + StaticInvocationExprBuilder name(String name); + StaticInvocationExprBuilder desc(String desc); +} diff --git a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/code/expr/invoke/StaticInvocationExpr.java b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/code/expr/invoke/StaticInvocationExpr.java index 659bd79a..a42a94a0 100644 --- a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/code/expr/invoke/StaticInvocationExpr.java +++ b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/code/expr/invoke/StaticInvocationExpr.java @@ -2,7 +2,9 @@ import org.mapleir.app.service.InvocationResolver; import org.mapleir.ir.code.Expr; +import org.mapleir.ir.code.Opcode; import org.mapleir.stdlib.collections.CollectionUtils; +import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.mapleir.asm.MethodNode; @@ -15,6 +17,10 @@ public StaticInvocationExpr(Expr[] args, String owner, String name, String desc) super(CallType.STATIC, args, owner, name, desc); } + public StaticInvocationExpr(CallType callType, Expr[] args, String owner, String name, String desc) { + super(callType, args, owner, name, desc); + } + @Override public StaticInvocationExpr copy() { return new StaticInvocationExpr(copyArgs(), getOwner(), getName(), getDesc()); @@ -32,7 +38,13 @@ public boolean isDynamic() { @Override protected void generateCallCode(MethodVisitor visitor) { - visitor.visitMethodInsn(Opcodes.INVOKESTATIC, getOwner(), getName(), getDesc(), getCallType() == CallType.INTERFACE); + visitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + getOwner(), + getName(), + getDesc(), + getCallType() == CallType.INTERFACE + ); } @Override diff --git a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/code/stmt/NopStmt.java b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/code/stmt/NopStmt.java index 14d272d2..4eeeaeed 100644 --- a/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/code/stmt/NopStmt.java +++ b/maple-ir/org.mapleir.ir/src/main/java/org/mapleir/ir/code/stmt/NopStmt.java @@ -5,6 +5,7 @@ import org.mapleir.ir.codegen.BytecodeFrontend; import org.mapleir.stdlib.util.TabbedStringWriter; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; public class NopStmt extends Stmt { public NopStmt() { @@ -23,6 +24,7 @@ public void toString(TabbedStringWriter printer) { @Override public void toCode(MethodVisitor visitor, BytecodeFrontend assembler) { + visitor.visitInsn(Opcodes.NOP); } @Override diff --git a/maple-ir/org.mapleir.main/src/main/java/org/mapleir/Boot.java b/maple-ir/org.mapleir.main/src/main/java/org/mapleir/Boot.java index 47e8c211..2362e108 100644 --- a/maple-ir/org.mapleir.main/src/main/java/org/mapleir/Boot.java +++ b/maple-ir/org.mapleir.main/src/main/java/org/mapleir/Boot.java @@ -24,6 +24,7 @@ import org.mapleir.ir.codegen.ControlFlowGraphDumper; import org.mapleir.asm.ClassNode; import org.mapleir.asm.MethodNode; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarInfo; import org.topdank.byteio.in.SingleJarDownloader; @@ -34,6 +35,7 @@ import java.util.*; import java.util.Map.Entry; import java.util.jar.JarOutputStream; +import java.util.stream.Collectors; public class Boot { @@ -63,7 +65,10 @@ public static void main(String[] args) throws Exception { SingleJarDownloader dl = new SingleJarDownloader<>(new JarInfo(f)); dl.download(); String appName = f.getName().substring(0, f.getName().length() - 4); - ApplicationClassSource app = new ApplicationClassSource(appName, dl.getJarContents().getClassContents()); + ApplicationClassSource app = new ApplicationClassSource( + appName, + dl.getJarContents().getClassContents().stream().map(JarClassData::getClassNode).collect(Collectors.toList()) + ); // // ApplicationClassSource app = new ApplicationClassSource("test", ClassHelper.parseClasses(CGExample.class)); // app.addLibraries(new InstalledRuntimeClassSource(app)); diff --git a/maple-ir/org.mapleir.main/src/main/java/org/mapleir/Boot2.java b/maple-ir/org.mapleir.main/src/main/java/org/mapleir/Boot2.java index acd603d4..12647d66 100644 --- a/maple-ir/org.mapleir.main/src/main/java/org/mapleir/Boot2.java +++ b/maple-ir/org.mapleir.main/src/main/java/org/mapleir/Boot2.java @@ -24,6 +24,7 @@ import org.mapleir.ir.codegen.ControlFlowGraphDumper; import org.mapleir.asm.ClassNode; import org.mapleir.asm.MethodNode; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarInfo; import org.topdank.byteio.in.SingleJarDownloader; @@ -31,6 +32,7 @@ import java.util.*; import java.util.Map.Entry; import java.util.jar.JarOutputStream; +import java.util.stream.Collectors; public class Boot2 { @@ -58,7 +60,10 @@ public static void main(String[] args) throws Exception { SingleJarDownloader dl = new SingleJarDownloader<>(new JarInfo(f)); dl.download(); String appName = f.getName().substring(0, f.getName().length() - 4); - ApplicationClassSource app = new ApplicationClassSource(appName, dl.getJarContents().getClassContents()); + ApplicationClassSource app = new ApplicationClassSource( + appName, + dl.getJarContents().getClassContents().stream().map(JarClassData::getClassNode).collect(Collectors.toList()) + ); // // ApplicationClassSource app = new ApplicationClassSource("test", ClassHelper.parseClasses(CGExample.class)); // app.addLibraries(new InstalledRuntimeClassSource(app)); diff --git a/maple-ir/org.mapleir.main/src/main/java/org/mapleir/DataFlowDemoBoot.java b/maple-ir/org.mapleir.main/src/main/java/org/mapleir/DataFlowDemoBoot.java index e545682c..9692fc02 100644 --- a/maple-ir/org.mapleir.main/src/main/java/org/mapleir/DataFlowDemoBoot.java +++ b/maple-ir/org.mapleir.main/src/main/java/org/mapleir/DataFlowDemoBoot.java @@ -21,6 +21,7 @@ import org.mapleir.stdlib.util.JavaDescUse; import org.mapleir.asm.ClassNode; import org.mapleir.asm.MethodNode; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarInfo; import org.topdank.byteio.in.SingleJarDownloader; @@ -28,6 +29,7 @@ import java.io.IOException; import java.util.*; import java.util.Map.Entry; +import java.util.stream.Collectors; public class DataFlowDemoBoot { @@ -58,7 +60,10 @@ public static void main(String[] args) throws Exception { SingleJarDownloader dl = new SingleJarDownloader<>(new JarInfo(f)); dl.download(); String appName = f.getName().substring(0, f.getName().length() - 4); - ApplicationClassSource app = new ApplicationClassSource(appName, dl.getJarContents().getClassContents()); + ApplicationClassSource app = new ApplicationClassSource( + appName, + dl.getJarContents().getClassContents().stream().map(JarClassData::getClassNode).collect(Collectors.toList()) + ); // // ApplicationClassSource app = new ApplicationClassSource("test", ClassHelper.parseClasses(CGExample.class)); // app.addLibraries(new InstalledcoRuntimeClassSource(app)); diff --git a/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/ClassHelper.java b/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/ClassHelper.java index ae128141..c1d6341e 100644 --- a/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/ClassHelper.java +++ b/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/ClassHelper.java @@ -10,8 +10,8 @@ import java.util.List; import java.util.Map; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.*; +import org.objectweb.asm.commons.JSRInlinerAdapter; /** * @author Bibl (don't ban me pls) @@ -41,7 +41,13 @@ public static ClassNode create(byte[] bytes) { public static ClassNode create(byte[] bytes, int flags) { ClassReader reader = new ClassReader(bytes); org.objectweb.asm.tree.ClassNode node = new org.objectweb.asm.tree.ClassNode(); - reader.accept(node, flags); + ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9, node) { + @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + return new JSRInlinerAdapter(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc, signature, exceptions); + } + }; + + reader.accept(visitor, flags); return new ClassNode(node); } diff --git a/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/ClassNode.java b/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/ClassNode.java index e3b22434..facb8c95 100644 --- a/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/ClassNode.java +++ b/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/ClassNode.java @@ -94,4 +94,19 @@ public boolean isPrivate() { public boolean isSynthetic() { return (node.access & Opcodes.ACC_SYNTHETIC) != 0; } + + public boolean isInterface() { + return (node.access & Opcodes.ACC_INTERFACE) != 0; + } + + public boolean isAnnoyingVersion() { + return (node.version & 0xFFFF) < Opcodes.V1_8; + } + + @Override + public String toString() { + return "ClassNode{" + + "node=" + node + + '}'; + } } diff --git a/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/MethodNode.java b/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/MethodNode.java index d0beeebf..c85df17b 100644 --- a/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/MethodNode.java +++ b/maple-ir/org.mapleir.modasm/src/main/java/org/mapleir/asm/MethodNode.java @@ -21,7 +21,7 @@ public MethodNode(org.objectweb.asm.tree.MethodNode node, ClassNode owner) { @Override public String toString() { - return (owner != null ? getOwner() : "null") + "." + getName() + getDesc(); + return (owner != null ? getOwner() : "null") + "#" + getName() + getDesc(); } @Override diff --git a/maple-ir/org.mapleir.stdlib/src/test/java/org/mapleir/stdlib/collections/graph/undirected/FakeFastUndirectedGraph.java b/maple-ir/org.mapleir.stdlib/src/test/java/org/mapleir/stdlib/collections/graph/undirected/FakeFastUndirectedGraph.java index 65d89490..417588db 100644 --- a/maple-ir/org.mapleir.stdlib/src/test/java/org/mapleir/stdlib/collections/graph/undirected/FakeFastUndirectedGraph.java +++ b/maple-ir/org.mapleir.stdlib/src/test/java/org/mapleir/stdlib/collections/graph/undirected/FakeFastUndirectedGraph.java @@ -4,6 +4,8 @@ import org.mapleir.stdlib.collections.graph.util.FakeFastEdge; import org.mapleir.stdlib.collections.graph.util.FakeFastVertex; +import java.util.Collection; + public class FakeFastUndirectedGraph extends FastUndirectedGraph { public FakeFastEdge pubGetSisterEdge(FakeFastEdge e) { @@ -18,7 +20,12 @@ public FakeFastEdge pubGetSisterEdge(FakeFastEdge e) { public FakeFastEdge clone(FakeFastEdge e, FakeFastVertex src, FakeFastVertex dst) { return new FakeFastEdge(src, dst, false); } - + + @Override + public Collection getCommonAncestor(Collection nodes) { + return null; + } + @Override public String toString() { return makeDotGraph().toString(); diff --git a/maple-ir/org.mapleir.stdlib/src/test/java/org/mapleir/stdlib/collections/graph/util/OrderedNode.java b/maple-ir/org.mapleir.stdlib/src/test/java/org/mapleir/stdlib/collections/graph/util/OrderedNode.java index 12e1725b..72371a65 100644 --- a/maple-ir/org.mapleir.stdlib/src/test/java/org/mapleir/stdlib/collections/graph/util/OrderedNode.java +++ b/maple-ir/org.mapleir.stdlib/src/test/java/org/mapleir/stdlib/collections/graph/util/OrderedNode.java @@ -68,5 +68,9 @@ public static class ODirectedGraph extends FastDirectedGraph implements OGraph { + @Override + public Collection getCommonAncestor(Collection nodes) { + return null; + } } } diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/classloader/JarClassLoader.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/classloader/JarClassLoader.java index f61fa42c..eb76f35e 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/classloader/JarClassLoader.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/classloader/JarClassLoader.java @@ -9,6 +9,7 @@ import org.mapleir.asm.ClassNode; import org.topdank.byteengineer.commons.asm.ASMFactory; import org.topdank.byteengineer.commons.asm.DefaultASMFactory; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.LocateableJarContents; /** @@ -25,14 +26,14 @@ public class JarClassLoader extends ClassLoader { private Map> cache; private ClassLoader ucp; - public JarClassLoader(LocateableJarContents contents) { + public JarClassLoader(LocateableJarContents contents) { this(contents, null, new DefaultASMFactory()); } /** * @param contents Reference to the JarContents object. */ - public JarClassLoader(LocateableJarContents contents, ClassLoader parent) { + public JarClassLoader(LocateableJarContents contents, ClassLoader parent) { this(contents, parent, new DefaultASMFactory()); } @@ -41,7 +42,7 @@ public JarClassLoader(LocateableJarContents contents, ClassLoader par * @param parent * @param factory ASMFactory. */ - public JarClassLoader(LocateableJarContents contents, ClassLoader parent, ASMFactory factory) { + public JarClassLoader(LocateableJarContents contents, ClassLoader parent, ASMFactory factory) { this.factory = factory; ClassLoader _parent = parent; @@ -74,12 +75,12 @@ private static StackTraceElement creator(boolean cl) { return null; } - public void add(LocateableJarContents contents) { + public void add(LocateableJarContents contents) { for (URL url : contents.getJarUrls()) { URLClassLoaderUtil.addLast(url, ucp); } - for (ClassNode cn : contents.getClassContents()) { - fastNodeCache.put(cn.getName(), cn); + for (JarClassData cn : contents.getClassContents()) { + fastNodeCache.put(cn.getName(), cn.getClassNode()); } } diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/JarClassData.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/JarClassData.java new file mode 100644 index 00000000..5019a708 --- /dev/null +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/JarClassData.java @@ -0,0 +1,58 @@ +package org.topdank.byteengineer.commons.data; + +import org.mapleir.asm.ClassNode; + +import java.util.Arrays; + +public final class JarClassData { + + private final String name; + private final byte[] data; + private final ClassNode classNode; + + public JarClassData(String name, byte[] data, ClassNode classNode) { + this.name = name; + this.data = data; + this.classNode = classNode; + } + + public String getName() { + return name; + } + + public byte[] getData() { + return data; + } + + public ClassNode getClassNode() { + return classNode; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + Arrays.hashCode(data); + result = (prime * result) + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + JarClassData other = (JarClassData) obj; + if (!Arrays.equals(data, other.data)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } +} \ No newline at end of file diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/JarContents.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/JarContents.java index 388df3d0..3bf2870e 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/JarContents.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/JarContents.java @@ -9,65 +9,57 @@ import org.mapleir.asm.ClassNode; -public class JarContents { +public class JarContents { - private final DataContainer classContents; - private final DataContainer classData; + private final DataContainer classContents; private final DataContainer resourceContents; public JarContents() { - classContents = new ClassNodeContainer(); - classData = new ResourceContainer(); + classContents = new ClassNodeContainer(); resourceContents = new ResourceContainer(); } - public JarContents(DataContainer classContents, DataContainer classData, DataContainer resourceContents) { - this.classContents = classContents == null ? new ClassNodeContainer() : classContents; - this.classData = classData == null ? new ResourceContainer() : classData; + public JarContents(DataContainer classContents, DataContainer classData, DataContainer resourceContents) { + this.classContents = classContents == null ? new ClassNodeContainer() : classContents; this.resourceContents = resourceContents == null ? new ResourceContainer() : resourceContents; } - public final DataContainer getClassContents() { + public final DataContainer getClassContents() { return classContents; } - - public final DataContainer getClassData() { - return classData; - } - public final DataContainer getResourceContents() { return resourceContents; } - public void merge(JarContents contents) { + public void merge(JarContents contents) { classContents.addAll(contents.classContents); resourceContents.addAll(contents.resourceContents); } - public JarContents add(JarContents contents) { - List c1 = classContents; - List c2 = contents.classContents; + public JarContents add(JarContents contents) { + List c1 = classContents; + List c2 = contents.classContents; List r1 = resourceContents; List r2 = contents.resourceContents; - List c3 = new ArrayList(c1.size() + c2.size()); + List c3 = new ArrayList<>(c1.size() + c2.size()); c3.addAll(c1); c3.addAll(c2); - List r3 = new ArrayList(r1.size() + r2.size()); + List r3 = new ArrayList<>(r1.size() + r2.size()); r3.addAll(r1); r3.addAll(r2); // TODO add jar data here - return new JarContents(new ClassNodeContainer<>(c3), null, new ResourceContainer(r3)); + return new JarContents(new ClassNodeContainer(c3), null, new ResourceContainer(r3)); } - public static class ClassNodeContainer extends DataContainer { + public static class ClassNodeContainer extends DataContainer { private static final long serialVersionUID = -6169578803641192235L; - private Map lastMap = new HashMap(); + private Map lastMap = new HashMap<>(); private boolean invalidated; public ClassNodeContainer() { @@ -78,18 +70,18 @@ public ClassNodeContainer(int cap) { super(cap); } - public ClassNodeContainer(Collection data) { + public ClassNodeContainer(Collection data) { super(data); } @Override - public boolean add(C c) { + public boolean add(JarClassData c) { invalidated = true; return super.add(c); } @Override - public boolean addAll(Collection c) { + public boolean addAll(Collection c) { invalidated = true; return super.addAll(c); } @@ -101,13 +93,13 @@ public boolean remove(Object c) { } @Override - public Map namedMap() { + public Map namedMap() { if (invalidated) { invalidated = false; - Map nodeMap = new HashMap(); - Iterator it = iterator(); + Map nodeMap = new HashMap<>(); + Iterator it = iterator(); while (it.hasNext()) { - C cn = it.next(); + JarClassData cn = it.next(); if (nodeMap.containsKey(cn.getName())) { it.remove(); } else { diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/LocateableJarContents.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/LocateableJarContents.java index e69c2be6..0c3f4d64 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/LocateableJarContents.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteengineer/commons/data/LocateableJarContents.java @@ -4,7 +4,7 @@ import org.mapleir.asm.ClassNode; -public class LocateableJarContents extends JarContents { +public class LocateableJarContents extends JarContents { private final URL[] jarUrls; @@ -13,7 +13,7 @@ public LocateableJarContents(URL... jarUrls) { this.jarUrls = jarUrls; } - public LocateableJarContents(DataContainer classContents, DataContainer resourceContents, URL... jarUrls) { + public LocateableJarContents(DataContainer classContents, DataContainer resourceContents, URL... jarUrls) { super(classContents, null, resourceContents); this.jarUrls = jarUrls; } diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/AbstractJarDownloader.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/AbstractJarDownloader.java index 9d0a5b17..1b313c57 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/AbstractJarDownloader.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/AbstractJarDownloader.java @@ -10,7 +10,7 @@ public abstract class AbstractJarDownloader { protected final ASMFactory factory; - protected LocateableJarContents contents; + protected LocateableJarContents contents; @SuppressWarnings("unchecked") public AbstractJarDownloader() { @@ -23,7 +23,7 @@ public AbstractJarDownloader(ASMFactory factory) { public abstract void download() throws IOException; - public LocateableJarContents getJarContents() { + public LocateableJarContents getJarContents() { return contents; } } diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/MultiJarDownloader.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/MultiJarDownloader.java index d7ca3862..a00cd241 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/MultiJarDownloader.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/MultiJarDownloader.java @@ -13,6 +13,7 @@ import com.google.common.io.ByteStreams; import org.mapleir.asm.ClassNode; import org.topdank.byteengineer.commons.asm.ASMFactory; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarInfo; import org.topdank.byteengineer.commons.data.JarResource; import org.topdank.byteengineer.commons.data.LocateableJarContents; @@ -38,7 +39,7 @@ public void download() throws IOException { for (int i = 0; i < jarInfos.length; i++) { urls[i] = new URL(jarInfos[i].formattedURL()); } - contents = new LocateableJarContents(urls); + contents = new LocateableJarContents(urls); for (JarInfo jarinfo : jarInfos) { JarURLConnection connection = (JarURLConnection) new URL(jarinfo.formattedURL()).openConnection(); JarFile jarFile = connection.getJarFile(); @@ -52,7 +53,11 @@ public void download() throws IOException { if (entry.getName().endsWith(".class")) { C cn = factory.create(bytes, entry.getName()); if(!map.containsKey(cn.getName())) { - contents.getClassContents().add(cn); + contents.getClassContents().add(new JarClassData( + entry.getName(), + bytes, + cn + )); } else { throw new IllegalStateException("duplicate: " + cn.getName()); } diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/SingleJarDownloader.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/SingleJarDownloader.java index a5f11138..77c79830 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/SingleJarDownloader.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/SingleJarDownloader.java @@ -12,6 +12,7 @@ import org.mapleir.asm.ClassNode; import org.topdank.byteengineer.commons.asm.ASMFactory; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarInfo; import org.topdank.byteengineer.commons.data.JarResource; import org.topdank.byteengineer.commons.data.LocateableJarContents; @@ -38,7 +39,7 @@ public void download() throws IOException { JarURLConnection connection = (JarURLConnection) (url = new URL(jarInfo.formattedURL())).openConnection(); JarFile jarFile = connection.getJarFile(); Enumeration entries = jarFile.entries(); - contents = new LocateableJarContents<>(url); + contents = new LocateableJarContents(url); Map map = new HashMap<>(); @@ -48,7 +49,11 @@ public void download() throws IOException { if (entry.getName().endsWith(".class")) { C cn = factory.create(bytes, entry.getName()); if(!map.containsKey(cn.getName())) { - contents.getClassContents().add(cn); + contents.getClassContents().add(new JarClassData( + entry.getName(), + bytes, + cn + )); } else { throw new IllegalStateException("duplicate: " + cn.getName()); } diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/SingleJmodDownloader.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/SingleJmodDownloader.java new file mode 100644 index 00000000..944d6a8b --- /dev/null +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/in/SingleJmodDownloader.java @@ -0,0 +1,76 @@ +package org.topdank.byteio.in; + +import com.google.common.io.ByteStreams; +import org.mapleir.asm.ClassNode; +import org.topdank.byteengineer.commons.asm.ASMFactory; +import org.topdank.byteengineer.commons.data.JarClassData; +import org.topdank.byteengineer.commons.data.JarInfo; +import org.topdank.byteengineer.commons.data.JarResource; +import org.topdank.byteengineer.commons.data.LocateableJarContents; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class SingleJmodDownloader extends AbstractJarDownloader { + + protected final JarInfo jarInfo; + + public SingleJmodDownloader(JarInfo jarInfo) { + super(); + this.jarInfo = jarInfo; + } + + public SingleJmodDownloader(ASMFactory factory, JarInfo jarInfo) { + super(factory); + this.jarInfo = jarInfo; + } + + @Override + public void download() throws IOException { + File file; + try (ZipFile jarFile = new ZipFile((file = new File(jarInfo.getPath())))) { + Enumeration entries = jarFile.entries(); + contents = new LocateableJarContents(file.toURI().toURL()); + + Map map = new HashMap<>(); + + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + byte[] bytes = read(jarFile.getInputStream(entry)); + if (entry.getName().endsWith(".class")) { + C cn = factory.create(bytes, entry.getName()); + if(!map.containsKey(cn.getName())) { + contents.getClassContents().add(new JarClassData( + entry.getName(), + bytes, + cn + )); + } else { + throw new IllegalStateException("duplicate: " + cn.getName()); + } + + //if(cn.name.equals("org/xmlpull/v1/XmlPullParser")) { + // System.out.println("SingleJarDownloader.download() " +entry.getName() + " " + bytes.length); + //} + } else { + JarResource resource = new JarResource(entry.getName(), bytes); + contents.getResourceContents().add(resource); + } + } + } + } + + private byte[] read(InputStream inputStream) throws IOException { + return ByteStreams.toByteArray(inputStream); + } +} diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/CompleteJarDumper.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/CompleteJarDumper.java index c8ec4121..f2ccaa66 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/CompleteJarDumper.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/CompleteJarDumper.java @@ -9,6 +9,7 @@ import org.mapleir.asm.ClassHelper; import org.objectweb.asm.ClassWriter; import org.mapleir.asm.ClassNode; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarContents; import org.topdank.byteengineer.commons.data.JarResource; import org.topdank.byteio.util.Debug; @@ -20,14 +21,14 @@ */ public class CompleteJarDumper implements JarDumper { - private final JarContents contents; + private final JarContents contents; /** * Creates a new JarDumper. * * @param contents Contents of jar. */ - public CompleteJarDumper(JarContents contents) { + public CompleteJarDumper(JarContents contents) { this.contents = contents; } @@ -44,8 +45,8 @@ public void dump(File file) throws IOException { JarOutputStream jos = new JarOutputStream(new FileOutputStream(file)); int classesDumped = 0; int resourcesDumped = 0; - for (ClassNode cn : contents.getClassContents()) { - classesDumped += dumpClass(jos, cn.getName(), cn); + for (JarClassData cn : contents.getClassContents()) { + classesDumped += dumpClass(jos, cn); } for (JarResource res : contents.getResourceContents()) { resourcesDumped += dumpResource(jos, res.getName(), res.getData()); @@ -60,16 +61,15 @@ public void dump(File file) throws IOException { * Writes the {@link ClassNode} to the Jar. * * @param out The {@link JarOutputStream}. - * @param cn The ClassNode. - * @param name The entry name. + * @param classData The {@link JarClassData}. * @throws IOException If there is a write error. * @return The amount of things dumped, 1 or if you're not dumping it 0. */ @Override - public int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOException { - JarEntry entry = new JarEntry(cn.getName() + ".class"); + public int dumpClass(JarOutputStream out, JarClassData classData) throws IOException { + JarEntry entry = new JarEntry(classData.getClassNode().getName() + ".class"); out.putNextEntry(entry); - out.write(ClassHelper.toByteArray(cn, ClassWriter.COMPUTE_FRAMES)); + out.write(ClassHelper.toByteArray(classData.getClassNode(), ClassWriter.COMPUTE_FRAMES)); return 1; } diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/JarDumper.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/JarDumper.java index 94d2b76a..af14dda8 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/JarDumper.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/JarDumper.java @@ -5,12 +5,13 @@ import java.util.jar.JarOutputStream; import org.mapleir.asm.ClassNode; +import org.topdank.byteengineer.commons.data.JarClassData; -public abstract interface JarDumper { +public interface JarDumper { - public void dump(File file) throws IOException; + void dump(File file) throws IOException; - public abstract int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOException; + int dumpClass(JarOutputStream out, JarClassData classData) throws IOException; - public abstract int dumpResource(JarOutputStream out, String name, byte[] file) throws IOException; + int dumpResource(JarOutputStream out, String name, byte[] file) throws IOException; } //? diff --git a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/NonMetaJarDumper.java b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/NonMetaJarDumper.java index 4fa74a9b..d82ee5ac 100644 --- a/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/NonMetaJarDumper.java +++ b/maple-ir/org.mapleir.topdank-services/src/main/java/org/topdank/byteio/out/NonMetaJarDumper.java @@ -8,7 +8,7 @@ public class NonMetaJarDumper extends CompleteJarDumper { - public NonMetaJarDumper(JarContents contents) { + public NonMetaJarDumper(JarContents contents) { super(contents); } diff --git a/obfuscator/dependency-reduced-pom.xml b/obfuscator/dependency-reduced-pom.xml index 2e6c8f3f..b87311c1 100644 --- a/obfuscator/dependency-reduced-pom.xml +++ b/obfuscator/dependency-reduced-pom.xml @@ -46,6 +46,18 @@ 1.18.22 provided + + com.github.xxDark + SSVM + 1.7.1 + test + + + CAFED00D + com.github.Col-E + + + 3.21.0 diff --git a/obfuscator/pom.xml b/obfuscator/pom.xml index cc9aa29a..06aac7a2 100644 --- a/obfuscator/pom.xml +++ b/obfuscator/pom.xml @@ -112,6 +112,12 @@ jline ${jline.version} + + com.github.xxDark + SSVM + 1.7.1 + test + \ No newline at end of file diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java index 40007cd7..960a1249 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java @@ -19,7 +19,7 @@ import dev.skidfuscator.obfuscator.hierarchy.Hierarchy; import dev.skidfuscator.obfuscator.hierarchy.SkidHierarchy; import dev.skidfuscator.obfuscator.order.OrderAnalysis; -import dev.skidfuscator.obfuscator.phantom.PhantomJarDownloader; +import dev.skidfuscator.obfuscator.phantom.jphantom.PhantomJarDownloader; import dev.skidfuscator.obfuscator.predicate.PredicateAnalysis; import dev.skidfuscator.obfuscator.predicate.SimplePredicateAnalysis; import dev.skidfuscator.obfuscator.predicate.factory.PredicateFactory; @@ -38,7 +38,6 @@ import dev.skidfuscator.obfuscator.transform.impl.SwitchTransformer; import dev.skidfuscator.obfuscator.transform.impl.flow.BasicConditionTransformer; import dev.skidfuscator.obfuscator.transform.impl.flow.BasicExceptionTransformer; -import dev.skidfuscator.obfuscator.transform.impl.flow.FlatteningFlowTransformer; import dev.skidfuscator.obfuscator.transform.impl.misc.AhegaoTransformer; import dev.skidfuscator.obfuscator.transform.impl.number.NumberTransformer; import dev.skidfuscator.obfuscator.transform.impl.string.StringTransformer; @@ -61,15 +60,17 @@ import org.mapleir.deob.PassGroup; import org.mapleir.deob.dataflow.LiveDataFlowAnalysisImpl; import org.mapleir.ir.cfg.ControlFlowGraph; -import org.objectweb.asm.Opcodes; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteio.in.AbstractJarDownloader; import org.topdank.byteio.in.SingleJarDownloader; +import org.topdank.byteio.in.SingleJmodDownloader; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; @Getter public class Skidfuscator { @@ -98,7 +99,7 @@ public Skidfuscator(SkidfuscatorSession session) { public void run() { LOGGER.post("Beginning Skidfuscator Enterprise..."); SkiddedDirectory.init(null); - this.irFactory = new SkidCache(); + this.irFactory = new SkidCache(this); this.exemptAnalysis = new SimpleExemptAnalysis(); LOGGER.post("Resolving predicate analysis..."); @@ -184,7 +185,7 @@ public ClassOpaquePredicate build(SkidClassNode skidClassNode) { /* Create a new library class source with superior to default priority */ final ApplicationClassSource libraryClassSource = new ApplicationClassSource( "libraries", - jar.getJarContents().getClassContents() + jar.getJarContents().getClassContents().stream().map(JarClassData::getClassNode).collect(Collectors.toList()) ); LOGGER.post("Imported " + jar.getJarContents().getClassContents().size() + " library classes..."); @@ -213,7 +214,7 @@ public ClassOpaquePredicate build(SkidClassNode skidClassNode) { this.classSource.addLibraries(new LibraryClassSource( new ApplicationClassSource( "phantom", - downloader.getPhantomContents().getClassContents() + downloader.getPhantomContents().getClassContents().stream().map(JarClassData::getClassNode).collect(Collectors.toList()) ), -1 )); @@ -221,28 +222,44 @@ public ClassOpaquePredicate build(SkidClassNode skidClassNode) { /* Import JVM */ LOGGER.post("Beginning importing of the JVM..."); - final SingleJarDownloader libs = MapleJarUtil.importJar( - session.getRuntime() - ); - this.classSource.addLibraries((jvmClassSource = new LibraryClassSource( - new ApplicationClassSource( - "runtime", - libs.getJarContents().getClassContents() - ), - 0 - ))); - LOGGER.log("Finished importing the JVM!"); - - if (session.getLibs() != null) { - final ClassNode classNode = classSource.findClassNode("org/json/simple/parser/ParseException"); - if (classNode == null) { - System.err.println("HAHAHHAHAHAH"); - for (ClassNode vertex : classSource.iterateWithLibraries()) { - if (classSource.isLibraryClass(vertex.getName()) && vertex.getName().contains("org") && !vertex.getName().contains("sun")) - System.out.println(vertex.getDisplayName()); - } + if (!session.isJmod()) { + final SingleJarDownloader libs = MapleJarUtil.importJar( + session.getRuntime() + ); + this.classSource.addLibraries((jvmClassSource = new LibraryClassSource( + new ApplicationClassSource( + "runtime", + libs.getJarContents() + .getClassContents() + .stream() + .map(JarClassData::getClassNode) + .collect(Collectors.toList()) + ), + 0 + ))); + } else { + for (File file : session.getRuntime().listFiles()) { + if (!file.getAbsolutePath().endsWith(".jmod")) + continue; + LOGGER.post("↳ Trying to download " + file.toString()); + final SingleJmodDownloader libs = MapleJarUtil.importJmod( + file + ); + this.classSource.addLibraries((jvmClassSource = new LibraryClassSource( + new ApplicationClassSource( + file.getName(), + libs.getJarContents().getClassContents() + .stream() + .map(JarClassData::getClassNode) + .collect(Collectors.toList()) + ), + 0 + ))); + LOGGER.post("✓ Success"); } + } + LOGGER.log("Finished importing the JVM!"); /* Resolve context */ LOGGER.post("Resolving basic context..."); @@ -274,7 +291,7 @@ public ClassOpaquePredicate build(SkidClassNode skidClassNode) { */ for (Listener o : Arrays.asList( new StringTransformer(this), - new NegationTransformer(this), + //new NegationTransformer(this), //new FlatteningFlowTransformer(this), new NumberTransformer(this), new SwitchTransformer(this), @@ -300,25 +317,17 @@ public ClassOpaquePredicate build(SkidClassNode skidClassNode) { try(ProgressBar progressBar = ProgressUtil.progress(cxt.getIRCache().size())) { for(Map.Entry e : new HashSet<>(cxt.getIRCache().entrySet())) { MethodNode mn = e.getKey(); - - if (exemptAnalysis.isExempt(mn.owner)) { - progressBar.step(); - continue; - } - - if (exemptAnalysis.isExempt(mn)) { - progressBar.step(); - continue; - } - ControlFlowGraph cfg = e.getValue(); try { cfg.verify(); + (new SkidFlowGraphDumper(this, cfg, mn)).dump(); } catch (Exception ex){ + if (ex instanceof IllegalStateException) { + throw ex; + } ex.printStackTrace(); } - (new SkidFlowGraphDumper(this, cfg, mn)).dump(); progressBar.step(); } } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java index a19966e1..bf6c355f 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorMain.java @@ -49,6 +49,7 @@ public static void main(String[] args) { null, null, new File(System.getProperty("java.home"), "lib/rt.jar"), + false, false ); diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorSession.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorSession.java index 43a9514e..9c344b69 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorSession.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/SkidfuscatorSession.java @@ -14,4 +14,5 @@ public class SkidfuscatorSession { private File exempt; private File runtime; private boolean phantom; + private boolean jmod; } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/command/ObfuscateCommand.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/command/ObfuscateCommand.java index 5731f4b2..6715403b 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/command/ObfuscateCommand.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/command/ObfuscateCommand.java @@ -58,6 +58,12 @@ public class ObfuscateCommand implements Callable { ) private boolean phantom; + @CommandLine.Option( + names = {"-jm", "--jmod"}, + description = "Declare if jmod computation should be used" + ) + private boolean jmod; + @Override public Integer call() { final String[] logo = new String[] { @@ -99,6 +105,7 @@ public Integer call() { .runtime(runtime) .exempt(exempt) .phantom(phantom) + .jmod(jmod) .build(); final Skidfuscator skidfuscator = new Skidfuscator(skidInstance); diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidApplicationClassSource.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidApplicationClassSource.java index df6a87b5..8a65f59e 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidApplicationClassSource.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidApplicationClassSource.java @@ -2,19 +2,21 @@ import org.mapleir.app.service.ApplicationClassSource; import org.mapleir.asm.ClassNode; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.LocateableJarContents; import java.util.Collection; import java.util.Map; +import java.util.stream.Collectors; public class SkidApplicationClassSource extends ApplicationClassSource { //private final LocateableJarContents nodes; - public SkidApplicationClassSource(String name, LocateableJarContents nodes) { - super(name, nodes.getClassContents()); + public SkidApplicationClassSource(String name, LocateableJarContents nodes) { + super(name, nodes.getClassContents().stream().map(JarClassData::getClassNode).collect(Collectors.toList())); } - public SkidApplicationClassSource(String name, Map nodeMap, LocateableJarContents nodes) { + public SkidApplicationClassSource(String name, Map nodeMap, LocateableJarContents nodes) { super(name, nodeMap); } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidCache.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidCache.java index 9b2a477f..03c8a4bc 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidCache.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidCache.java @@ -1,9 +1,10 @@ package dev.skidfuscator.obfuscator.creator; +import dev.skidfuscator.obfuscator.Skidfuscator; import org.mapleir.context.IRCache; public class SkidCache extends IRCache { - public SkidCache() { - super(SkidFlowGraphBuilder::build); + public SkidCache(final Skidfuscator skidfuscator) { + super(e -> SkidFlowGraphBuilder.build(skidfuscator, e)); } } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphBuilder.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphBuilder.java index e7e0abb8..e777151a 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphBuilder.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphBuilder.java @@ -1,12 +1,13 @@ package dev.skidfuscator.obfuscator.creator; +import dev.skidfuscator.obfuscator.Skidfuscator; import dev.skidfuscator.obfuscator.skidasm.cfg.SkidBlockFactory; import org.mapleir.asm.MethodNode; import org.mapleir.ir.algorithms.BoissinotDestructor; import org.mapleir.ir.algorithms.LocalsReallocator; import org.mapleir.ir.cfg.ControlFlowGraph; import org.mapleir.ir.cfg.SSAFactory; -import org.mapleir.ir.cfg.builder.ControlFlowGraphBuilder; +import org.mapleir.ir.cfg.builder.*; public class SkidFlowGraphBuilder extends ControlFlowGraphBuilder { @@ -22,8 +23,18 @@ public SkidFlowGraphBuilder(MethodNode method, SSAFactory SSAFactory, boolean op super(method, SSAFactory, optimise); } - public static ControlFlowGraph build(MethodNode method) { - ControlFlowGraphBuilder builder = new SkidFlowGraphBuilder(method, SkidBlockFactory.INSTANCE); + @Override + protected BuilderPass[] resolvePasses() { + return new BuilderPass[] { + new GenerationPass(this), + new DeadBlocksPass(this), + //new NaturalisationPass(this), + new SSAGenPass(this, false), + }; + } + + public static ControlFlowGraph build(final Skidfuscator skidfuscator, final MethodNode method) { + ControlFlowGraphBuilder builder = new SkidFlowGraphBuilder(method, SkidBlockFactory.v(skidfuscator)); final ControlFlowGraph cfg = builder.buildImpl(); BoissinotDestructor.leaveSSA(cfg); LocalsReallocator.realloc(cfg); diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphDumper.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphDumper.java index 5d537e61..cb8d707c 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphDumper.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphDumper.java @@ -13,6 +13,7 @@ import org.mapleir.ir.cfg.BasicBlock; import org.mapleir.ir.cfg.ControlFlowGraph; import org.mapleir.ir.code.Stmt; +import org.mapleir.ir.code.stmt.NopStmt; import org.mapleir.ir.code.stmt.UnconditionalJumpStmt; import org.mapleir.ir.codegen.BytecodeFrontend; import org.mapleir.stdlib.collections.graph.*; @@ -55,6 +56,9 @@ public void dump() { labels.put(b, new LabelNode()); } + // Fix ranges + fixRanges(); + // Linearize linearize(); @@ -84,7 +88,19 @@ public void dump() { m.node.visitEnd(); } - + + private void fixRanges() { + /* + * Short term fix to prevent TryCatchNode-s from being optimized + * out, leaving an open range + */ + for (ExceptionRange range : cfg.getRanges()) { + range.getNodes().stream().filter(BasicBlock::isEmpty).forEach(e -> { + e.add(new NopStmt()); + }); + } + } + private void linearize() { if (cfg.getEntries().size() != 1) throw new IllegalStateException("CFG doesn't have exactly 1 entry"); @@ -274,7 +290,7 @@ private void dumpRange(ExceptionRange er) { .findClassNode(type1); if (classNode == null) { - System.err.println("Failed to find class of type " + type1 + "!" ); + System.err.println("\r\nFailed to find class of type " + type1 + "!\n" ); try { final ClassReader reader = new ClassReader(type1); final org.objectweb.asm.tree.ClassNode node = new org.objectweb.asm.tree.ClassNode(); @@ -408,10 +424,12 @@ private void dumpRange(ExceptionRange er) { for (;;) { // check for endpoints if (orderIdx + 1 == order.size()) { // end of method + assert start != terminalLabel.getLabel() : "Label assigned is semantically identical."; m.node.visitTryCatchBlock(start, terminalLabel.getLabel(), handler, type.getInternalName()); break; } else if (rangeIdx + 1 == range.size()) { // end of range Label end = getLabel(order.get(orderIdx + 1)); + assert start != end : "Label assigned is semantically identical."; m.node.visitTryCatchBlock(start, end, handler, type.getInternalName()); break; } @@ -420,8 +438,9 @@ private void dumpRange(ExceptionRange er) { BasicBlock nextBlock = range.get(rangeIdx + 1); int nextOrderIdx = order.indexOf(nextBlock); if (nextOrderIdx - orderIdx > 1) { // blocks in-between, end the handler and begin anew - System.err.println("[warn] Had to split up a range: " + m); + System.err.println("\r\n[warn] Had to split up a range: " + m + "\n"); Label end = getLabel(order.get(orderIdx + 1)); + assert start != end : "Label assigned is semantically identical."; m.node.visitTryCatchBlock(start, end, handler, type.getInternalName()); start = getLabel(nextBlock); } @@ -452,6 +471,10 @@ private void verifyRanges() { handler = i; } } + + if (start == end) { + throw new IllegalStateException("Try block ends on starting position in " + m); + } if (start == -1 || end == -1 || handler == -1) throw new IllegalStateException("Try/catch endpoints missing: " + start + " " + end + " " + handler + m); } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/exempt/ExclusionHelper.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/exempt/ExclusionHelper.java index 4d104521..6a48d630 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/exempt/ExclusionHelper.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/exempt/ExclusionHelper.java @@ -67,7 +67,6 @@ public Exclusion renderExclusion(final String pattern) { switch (type) { case CLASS: { final Pattern regex = Pattern.compile(parsed); - System.out.println("Found pattern for clazz: [" + regex + "]"); map.put(type, new ExclusionTester() { @Override public boolean test(ClassNode var) { @@ -83,8 +82,6 @@ public boolean test(ClassNode var) { System.out.println("Oh?"); } - System.out.println("Testing if match: " + var.getName()); - return initialMatch && regex.matcher(var.getName()).find(); } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/exempt/SimpleExemptAnalysis.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/exempt/SimpleExemptAnalysis.java index 0ee6bbd6..13f5ee4b 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/exempt/SimpleExemptAnalysis.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/exempt/SimpleExemptAnalysis.java @@ -23,7 +23,7 @@ public void add(final String exclusionStr) { final Exclusion exclusion = ExclusionHelper.renderExclusion(exclusionStr); exclusions.add(exclusion); - System.out.println(this); + //System.out.println(this); } @Override @@ -57,11 +57,11 @@ public boolean isExempt(ClassNode classNode) { return var; for (Exclusion exclusion : exclusions) { - System.out.println("Testing " + exclusion); + //System.out.println("Testing " + exclusion); try { if (exclusion.test(classNode)) { classCache.put(classNode, true); - System.out.println("EXCLUDED --> " + classNode.getName()); + //System.out.println("EXCLUDED --> " + classNode.getName()); return true; } } catch (AssertionError e) { @@ -69,7 +69,7 @@ public boolean isExempt(ClassNode classNode) { e.printStackTrace(); } } - System.out.println("INCLUDED --> " + classNode.getName()); + //System.out.println("INCLUDED --> " + classNode.getName()); classCache.put(classNode, false); return false; } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/hierarchy/SkidHierarchy.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/hierarchy/SkidHierarchy.java index 9c7a3c28..7e216e17 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/hierarchy/SkidHierarchy.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/hierarchy/SkidHierarchy.java @@ -9,6 +9,10 @@ import org.mapleir.asm.MethodNode; import org.mapleir.ir.cfg.ControlFlowGraph; import org.mapleir.ir.code.expr.invoke.*; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.JSRInlinerAdapter; import org.objectweb.asm.tree.AnnotationNode; import java.util.*; @@ -131,7 +135,9 @@ public void cache() { this.annotations = new HashMap<>(); try (ProgressBar progressBar = ProgressUtil.progress(skidfuscator.getClassSource().size())){ - nodes = skidfuscator.getClassSource().getClassTree().vertices().parallelStream() + nodes = skidfuscator.getClassSource().getClassTree() + .vertices() + .parallelStream() .filter(e -> { progressBar.step(); return skidfuscator.getClassSource().isApplicationClass(e.getName()); @@ -167,13 +173,14 @@ private void setupInvoke() { try (ProgressBar invocationBar = ProgressUtil.progress(nodes.size())) { nodes.forEach(c -> { for (MethodNode method : c.getMethods()) { - final ControlFlowGraph cfg = skidfuscator.getCxt().getIRCache().get(method); + final ControlFlowGraph cfg = skidfuscator.getCxt().getIRCache().getFor(method); - if (cfg == null) + if (cfg == null) { + System.err.println("Failed to compute CFG for method " + method.toString()); continue; + } cfg.allExprStream() - .parallel() .filter(e -> e instanceof Invokable && !(e instanceof DynamicInvocationExpr)) .map(e -> (Invocation) e) .forEach(invocation -> { @@ -227,9 +234,15 @@ private SkidGroup getGroup(final Skidfuscator session, final MethodNode methodNo SkidGroup group = methodToGroupMap.get(methodNode); if (group == null) { - final Set h = session.getCxt() + final Set h = session + .getCxt() .getInvocationResolver() - .getHierarchyMethodChain(methodNode.owner, methodNode.getName(), methodNode.getDesc(), true); + .getHierarchyMethodChain( + methodNode.owner, + methodNode.getName(), + methodNode.getDesc(), + true + ); h.add(methodNode); final List methods = new ArrayList<>(h); diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomManager.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomManager.java deleted file mode 100644 index 3b009312..00000000 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomManager.java +++ /dev/null @@ -1,5 +0,0 @@ -package dev.skidfuscator.obfuscator.phantom; - -public class PhantomManager { - -} diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/Ghost.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/Ghost.java new file mode 100644 index 00000000..a22df7b9 --- /dev/null +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/Ghost.java @@ -0,0 +1,16 @@ +package dev.skidfuscator.obfuscator.phantom.jghost; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class Ghost { + private static final Gson GSON = new GsonBuilder() + .setPrettyPrinting() + .serializeNulls() + .disableHtmlEscaping() + .create(); + + public static Gson gson() { + return GSON; + } +} diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/GhostReader.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/GhostReader.java new file mode 100644 index 00000000..4eb89aee --- /dev/null +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/GhostReader.java @@ -0,0 +1,5 @@ +package dev.skidfuscator.obfuscator.phantom.jghost; + +public class GhostReader { + +} diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/tree/GhostClassNode.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/tree/GhostClassNode.java new file mode 100644 index 00000000..261f6b69 --- /dev/null +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/tree/GhostClassNode.java @@ -0,0 +1,7 @@ +package dev.skidfuscator.obfuscator.phantom.jghost.tree; + +import org.mapleir.asm.ClassNode; + +public class GhostClassNode extends ClassNode { + +} diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/tree/GhostMethodNode.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/tree/GhostMethodNode.java new file mode 100644 index 00000000..7ed7409e --- /dev/null +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jghost/tree/GhostMethodNode.java @@ -0,0 +1,10 @@ +package dev.skidfuscator.obfuscator.phantom.jghost.tree; + +import org.mapleir.asm.ClassNode; +import org.mapleir.asm.MethodNode; + +public class GhostMethodNode extends MethodNode { + public GhostMethodNode(org.objectweb.asm.tree.MethodNode node, ClassNode owner) { + super(node, owner); + } +} diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomJarDownloader.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomJarDownloader.java similarity index 91% rename from obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomJarDownloader.java rename to obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomJarDownloader.java index 720b9995..46e0bb12 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomJarDownloader.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomJarDownloader.java @@ -1,4 +1,4 @@ -package dev.skidfuscator.obfuscator.phantom; +package dev.skidfuscator.obfuscator.phantom.jphantom; import ch.qos.logback.classic.Level; import com.google.common.io.ByteStreams; @@ -26,10 +26,7 @@ import org.mapleir.asm.ClassNode; import org.objectweb.asm.*; import org.topdank.byteengineer.commons.asm.ASMFactory; -import org.topdank.byteengineer.commons.data.JarContents; -import org.topdank.byteengineer.commons.data.JarInfo; -import org.topdank.byteengineer.commons.data.JarResource; -import org.topdank.byteengineer.commons.data.LocateableJarContents; +import org.topdank.byteengineer.commons.data.*; import org.topdank.byteio.in.AbstractJarDownloader; import java.io.File; @@ -45,21 +42,21 @@ public class PhantomJarDownloader extends AbstractJarDownloader { private final Skidfuscator skidfuscator; protected final JarInfo jarInfo; - protected LocateableJarContents phantomContents; + protected LocateableJarContents phantomContents; private final Logger logger = LogManager.getLogger(this.getClass()); public PhantomJarDownloader(Skidfuscator skidfuscator, JarInfo jarInfo) { super(); this.skidfuscator = skidfuscator; this.jarInfo = jarInfo; - this.phantomContents = new LocateableJarContents<>(); + this.phantomContents = new LocateableJarContents(); } public PhantomJarDownloader(Skidfuscator skidfuscator, ASMFactory factory, JarInfo jarInfo) { super(factory); this.skidfuscator = skidfuscator; this.jarInfo = jarInfo; - this.phantomContents = new LocateableJarContents<>(); + this.phantomContents = new LocateableJarContents(); } @SneakyThrows @@ -69,7 +66,7 @@ public void download() throws IOException { JarURLConnection connection = (JarURLConnection) (url = new URL(jarInfo.formattedURL())).openConnection(); JarFile jarFile = connection.getJarFile(); Enumeration entries = jarFile.entries(); - contents = new LocateableJarContents<>(url); + contents = new LocateableJarContents(url); /* * Map holding all the regular data @@ -85,12 +82,7 @@ public void download() throws IOException { byte[] bytes = read(jarFile.getInputStream(entry)); if (entry.getName().endsWith(".class")) { data.put(entry.getName(), bytes); - - System.out.println("[+] " + entry.getName()); - contents.getClassData().add(new JarResource( - entry.getName(), - bytes - )); + //System.out.println("[+] " + entry.getName()); } else { JarResource resource = new JarResource(entry.getName(), bytes); contents.getResourceContents().add(resource); @@ -110,7 +102,11 @@ public void download() throws IOException { try { cn = factory.create(db, name); if(!data.containsKey(cn.getName())) { - contents.getClassContents().add(cn); + contents.getClassContents().add(new JarClassData( + name, + db, + cn + )); } else { throw new IllegalStateException("duplicate: " + cn.getName()); } @@ -199,11 +195,12 @@ public void download() throws IOException { * class cache. This will be used as a library during obfuscation. */ logger.info("[$] Outputting phantom classes..."); - final Map namedMap = contents.getClassContents().namedMap(); + final Map namedMap = contents.getClassContents().namedMap(); try (ProgressBar progressBar = ProgressUtil.progress(phantom.getGenerated().size())){ phantom.getGenerated().forEach((k, v) -> { - final ClassReader reader = new ClassReader(decorate(v)); + final byte[] bytes = decorate(v); + final ClassReader reader = new ClassReader(bytes); final org.objectweb.asm.tree.ClassNode node = new org.objectweb.asm.tree.ClassNode(); reader.accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); @@ -213,7 +210,11 @@ public void download() throws IOException { if (namedMap.containsKey(classNode.getName())) return; - phantomContents.getClassContents().add((C) classNode); + phantomContents.getClassContents().add(new JarClassData( + classNode.getName() + ".class", + v, + classNode + )); progressBar.step(); }); } @@ -233,7 +234,7 @@ public void download() throws IOException { java.nio.file.Files.deleteIfExists(input.toPath()); } - public LocateableJarContents getPhantomContents() { + public LocateableJarContents getPhantomContents() { return phantomContents; } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomManager.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomManager.java new file mode 100644 index 00000000..efff1ace --- /dev/null +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomManager.java @@ -0,0 +1,5 @@ +package dev.skidfuscator.obfuscator.phantom.jphantom; + +public class PhantomManager { + +} diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomResolvingJarDumper.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomResolvingJarDumper.java similarity index 75% rename from obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomResolvingJarDumper.java rename to obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomResolvingJarDumper.java index 304d46ba..78ce82bb 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/PhantomResolvingJarDumper.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/phantom/jphantom/PhantomResolvingJarDumper.java @@ -1,8 +1,10 @@ -package dev.skidfuscator.obfuscator.phantom; +package dev.skidfuscator.obfuscator.phantom.jphantom; import com.google.common.collect.Lists; import dev.skidfuscator.obfuscator.Skidfuscator; import dev.skidfuscator.obfuscator.skidasm.SkidClassNode; +import dev.skidfuscator.obfuscator.util.ProgressUtil; +import me.tongfei.progressbar.ProgressBar; import org.mapleir.app.service.ApplicationClassSource; import org.mapleir.app.service.ClassTree; import org.mapleir.asm.ClassHelper; @@ -10,6 +12,7 @@ import org.mapleir.asm.MethodNode; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarContents; import org.topdank.byteengineer.commons.data.JarResource; import org.topdank.byteio.out.JarDumper; @@ -22,6 +25,8 @@ import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; +import java.util.zip.ZipError; +import java.util.zip.ZipException; /** * Dumps ClassNodes and JarResources back into a file on the local system. @@ -32,14 +37,14 @@ public class PhantomResolvingJarDumper implements JarDumper { private final Skidfuscator skidfuscator; - private final JarContents contents; + private final JarContents contents; private final ApplicationClassSource source; /** * Creates a new JarDumper. * * @param contents Contents of jar. */ - public PhantomResolvingJarDumper(Skidfuscator skidfuscator, JarContents contents, ApplicationClassSource source) { + public PhantomResolvingJarDumper(Skidfuscator skidfuscator, JarContents contents, ApplicationClassSource source) { this.skidfuscator = skidfuscator; this.contents = contents; this.source = source; @@ -58,12 +63,25 @@ public void dump(File file) throws IOException { JarOutputStream jos = new JarOutputStream(new FileOutputStream(file)); int classesDumped = 0; int resourcesDumped = 0; - for (ClassNode cn : contents.getClassContents()) { - classesDumped += dumpClass(jos, cn.getName(), cn); - } - for (JarResource res : contents.getResourceContents()) { - resourcesDumped += dumpResource(jos, res.getName(), res.getData()); + + try (ProgressBar progressBar = ProgressUtil.progress(contents.getClassContents().size() + contents.getResourceContents().size())) { + for (JarClassData cn : contents.getClassContents()) { + try { + classesDumped += dumpClass(jos, cn); + } catch (ZipException e) { + System.out.println("\r[!] Failed to dump " + cn.getName() + "!\n"); + throw e; + } + + progressBar.step(); + } + + for (JarResource res : contents.getResourceContents()) { + resourcesDumped += dumpResource(jos, res.getName(), res.getData()); + progressBar.step(); + } } + if(!Debug.debugging) System.out.println("Dumped " + classesDumped + " classes and " + resourcesDumped + " resources to " + file.getAbsolutePath()); @@ -74,14 +92,14 @@ public void dump(File file) throws IOException { * Writes the {@link ClassNode} to the Jar. * * @param out The {@link JarOutputStream}. - * @param cn The ClassNode. - * @param name The entry name. + * @param classData The {@link JarClassData} * @throws IOException If there is a write error. * @return The amount of things dumped, 1 or if you're not dumping it 0. */ @Override - public int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOException { - JarEntry entry = new JarEntry(cn.getName() + ".class"); + public int dumpClass(JarOutputStream out, JarClassData classData) throws IOException { + ClassNode cn = classData.getClassNode(); + JarEntry entry = new JarEntry(cn.getName()); out.putNextEntry(entry); if (skidfuscator.getExemptAnalysis().isExempt(cn)) { @@ -89,9 +107,9 @@ public int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOEx skidfuscator .getJarDownloader() .getJarContents() - .getClassData() + .getClassContents() .namedMap() - .get(cn.getName() + ".class") + .get(classData.getName()) .getData() ); return 1; @@ -114,8 +132,8 @@ public int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOEx ClassWriter writer = this.buildClassWriter(tree, ClassWriter.COMPUTE_MAXS); cn.node.accept(writer); // must use custom writer which overrides getCommonSuperclass out.write(writer.toByteArray()); - System.err.println("Failed to write " + cn.getName() + "! Writing with COMPUTE_MAXS, " + - "which may cause runtime abnormalities"); + System.err.println("\rFailed to write " + cn.getName() + "! Writing with COMPUTE_MAXS, " + + "which may cause runtime abnormalities\n"); } } catch (Exception e) { System.err.println("Failed to write " + cn.getName() + "! Skipping class..."); @@ -144,44 +162,37 @@ protected String getCommonSuperClass(String type1, String type2) { boolean debug = false; if(ccn == null) { -// return "java/lang/Object"; ClassNode c; try { final ClassReader reader = new ClassReader(type1); final org.objectweb.asm.tree.ClassNode node = new org.objectweb.asm.tree.ClassNode(); - reader.accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + reader.accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE); c = new SkidClassNode(node, skidfuscator); skidfuscator.getClassSource().getClassTree().addVertex(c); } catch (IOException e) { - e.printStackTrace(); - return "java/lang/Object"; - } - if(c == null) { + System.err.println("[FATAL] Failed to find common superclass due to failed " + type1); return "java/lang/Object"; } ccn = c; - debug = true; - //throw new UnsupportedOperationException(c.toString()); - // classTree.build(c); - // return getCommonSuperClass(type1, type2); } if(dcn == null) { - -// return "java/lang/Object"; ClassNode c; try { - c = ClassHelper.create(type2); + final ClassReader reader = new ClassReader(type1); + final org.objectweb.asm.tree.ClassNode node = new org.objectweb.asm.tree.ClassNode(); + reader.accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE); + + c = new SkidClassNode(node, skidfuscator); + skidfuscator.getClassSource().getClassTree().addVertex(c); } catch (IOException e) { - e.printStackTrace(); - return "java/lang/Object"; - } - if(c == null) { + System.err.println("[FATAL] Failed to find common superclass due to failed " + type1); return "java/lang/Object"; } - throw new UnsupportedOperationException(c.toString()); + + dcn = c; // classTree.build(c); // return getCommonSuperClass(type1, type2); } @@ -200,13 +211,14 @@ protected String getCommonSuperClass(String type1, String type2) { } if (true) - return skidfuscator.getClassSource().getClassTree() + return skidfuscator + .getClassSource() + .getClassTree() .getCommonAncestor(Arrays.asList(ccn, dcn)) .iterator() .next() .getName(); - { throw new IllegalStateException("Could not find common class type between " + Arrays.toString(new Object[]{ccn.getDisplayName(), dcn.getDisplayName()})); } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/predicate/renderer/impl/IntegerBlockPredicateRenderer.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/predicate/renderer/impl/IntegerBlockPredicateRenderer.java index 1f3cf991..c322f0d1 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/predicate/renderer/impl/IntegerBlockPredicateRenderer.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/predicate/renderer/impl/IntegerBlockPredicateRenderer.java @@ -68,7 +68,7 @@ void handle(final InitMethodTransformEvent event) { final Local local = methodNode .getCfg() .getLocals() - .get(methodNode.getCfg().getLocals().getMaxLocals() + 2); + .get(methodNode.getCfg().getLocals().getMaxLocals() + 3); flowPredicate.setGetter(new PredicateFlowGetter() { @Override @@ -248,7 +248,6 @@ public Stmt apply(Expr expr) { ); } }); - }; /* @@ -356,14 +355,13 @@ void handle(final InitGroupTransformEvent event) { return; } - final boolean entryPoint = skidGroup.getInvokers().isEmpty() - || skidGroup.isAnnotation(); + final boolean entryPoint = skidGroup.isEntryPoint(); Local local = null; int stackHeight = -1; String desc = null; if (entryPoint) { - System.err.println("SkidGroup " + skidGroup.getName() + "#" + skidGroup.getDesc() + " is an entry point!"); + //System.err.println("SkidGroup " + skidGroup.getName() + "#" + skidGroup.getDesc() + " is an entry point!"); } if (!entryPoint) { @@ -427,8 +425,6 @@ void handle(final InitGroupTransformEvent event) { invoker.getExpr().setArgumentExprs(args); invoker.getExpr().setDesc(desc); - - //System.out.println("Fixed invoker " + invoker.toString()); } } diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/SkidGroup.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/SkidGroup.java index b909e74f..381ea389 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/SkidGroup.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/SkidGroup.java @@ -22,16 +22,24 @@ public class SkidGroup { private String desc; private int stackHeight; + private transient boolean application; // TODO: Add parameter and parameter compilation public SkidGroup(List methodNodeList, Skidfuscator skidfuscator) { this.methodNodeList = methodNodeList; this.skidfuscator = skidfuscator; + this.invokers = new ArrayList<>(); this.predicate = skidfuscator .getPredicateAnalysis() .getMethodPredicate(this); - this.invokers = new ArrayList<>(); + + this.application = methodNodeList + .stream() + .allMatch(e -> skidfuscator.getClassSource().isApplicationClass(e.owner.getName()) + && !skidfuscator.getExemptAnalysis().isExempt(e) + && !skidfuscator.getExemptAnalysis().isExempt(e.owner) + ); } public void setStatical(boolean statical) { @@ -45,7 +53,7 @@ public MethodNode first() { } public boolean isEntryPoint() { - return this.getInvokers().isEmpty() || this.isAnnotation(); + return !application || this.getInvokers().isEmpty() || this.isAnnotation(); } @Override public boolean equals(Object o) { diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/SkidMethodNode.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/SkidMethodNode.java index 5c3ae59f..b5ba51f1 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/SkidMethodNode.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/SkidMethodNode.java @@ -28,10 +28,10 @@ public class SkidMethodNode extends MethodNode { public SkidMethodNode(org.objectweb.asm.tree.MethodNode node, ClassNode owner, Skidfuscator skidfuscator) { super(node, owner); this.skidfuscator = skidfuscator; + this.invokers = new ArrayList<>(); this.flowPredicate = skidfuscator .getPredicateAnalysis() .getBlockPredicate(this); - this.invokers = new ArrayList<>(); } public int getBlockPredicate(final SkidBlock block) { diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/cfg/SkidBlockFactory.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/cfg/SkidBlockFactory.java index 5871386a..31658040 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/cfg/SkidBlockFactory.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/skidasm/cfg/SkidBlockFactory.java @@ -1,22 +1,110 @@ package dev.skidfuscator.obfuscator.skidasm.cfg; +import dev.skidfuscator.obfuscator.Skidfuscator; import dev.skidfuscator.obfuscator.skidasm.expr.SkidConstantExpr; import dev.skidfuscator.obfuscator.skidasm.stmt.SkidSwitchStmt; +import org.mapleir.asm.ClassNode; import org.mapleir.ir.cfg.BasicBlock; import org.mapleir.ir.cfg.ControlFlowGraph; import org.mapleir.ir.cfg.DefaultBlockFactory; import org.mapleir.ir.cfg.builder.ssa.BlockBuilder; import org.mapleir.ir.cfg.builder.ssa.expr.ConstantExprBuilder; +import org.mapleir.ir.cfg.builder.ssa.expr.invoke.StaticInvocationExprBuilder; import org.mapleir.ir.cfg.builder.ssa.stmt.SwitchStmtBuilder; import org.mapleir.ir.code.Expr; import org.mapleir.ir.code.expr.ConstantExpr; +import org.mapleir.ir.code.expr.invoke.InvocationExpr; +import org.mapleir.ir.code.expr.invoke.StaticInvocationExpr; import org.mapleir.ir.code.stmt.SwitchStmt; import org.objectweb.asm.Type; import java.util.LinkedHashMap; public class SkidBlockFactory extends DefaultBlockFactory { - public static final SkidBlockFactory INSTANCE = new SkidBlockFactory(); + public static SkidBlockFactory INSTANCE = null; + + public static SkidBlockFactory v(final Skidfuscator skidfuscator) { + if (INSTANCE == null) { + INSTANCE = new SkidBlockFactory(skidfuscator); + } + return INSTANCE; + } + + private final Skidfuscator skidfuscator; + + public SkidBlockFactory(Skidfuscator skidfuscator) { + this.skidfuscator = skidfuscator; + } + + @Override + public StaticInvocationExprBuilder static_invoke_expr() { + return new StaticInvocationExprBuilder() { + private InvocationExpr.CallType callType = InvocationExpr.CallType.STATIC; + private Expr[] args; + private String owner; + private String name; + private String desc; + + @Override + public StaticInvocationExprBuilder callType(InvocationExpr.CallType callType) { + this.callType = callType; + return this; + } + + @Override + public StaticInvocationExprBuilder args(Expr[] args) { + this.args = args; + return this; + } + + @Override + public StaticInvocationExprBuilder owner(String owner) { + this.owner = owner; + return this; + } + + @Override + public StaticInvocationExprBuilder name(String name) { + this.name = name; + return this; + } + + @Override + public StaticInvocationExprBuilder desc(String desc) { + this.desc = desc; + return this; + } + + @Override + public StaticInvocationExpr build() { + assert owner != null : "Owner name cannot be null"; + assert name != null : "Name cannot be null"; + assert desc != null : "Description cannot be null"; + + final ClassNode classNode = skidfuscator + .getClassSource() + .findClassNode(owner); + + if (classNode == null) { + //System.out.println("Failed to find " + owner + " in reference path..."); + } else if (classNode.isInterface()){ + //System.out.println("Class " + owner + " is of version " + classNode.node.version + " (annoying: " + classNode.isAnnoyingVersion() + ")"); + } + + final boolean isInterface = classNode != null && classNode.isInterface(); + + return new StaticInvocationExpr( + isInterface + ? InvocationExpr.CallType.INTERFACE + : InvocationExpr.CallType.STATIC, + args, + owner, + name, + desc + ); + } + }; + } @Override public SwitchStmtBuilder switch_stmt() { diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/ClassUtil.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/ClassUtil.java index 3107fa18..792278ea 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/ClassUtil.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/ClassUtil.java @@ -2,6 +2,7 @@ import dev.skidfuscator.obfuscator.util.misc.Pair; import org.objectweb.asm.*; +import org.objectweb.asm.commons.JSRInlinerAdapter; import org.objectweb.asm.tree.*; import java.io.IOException; @@ -418,5 +419,4 @@ public static boolean isValidClass(byte[] value) { return false; } } - } \ No newline at end of file diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/MapleJarUtil.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/MapleJarUtil.java index 7c64e3e6..4e913c8b 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/MapleJarUtil.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/MapleJarUtil.java @@ -2,21 +2,19 @@ import dev.skidfuscator.obfuscator.Skidfuscator; import dev.skidfuscator.obfuscator.creator.SkidASMFactory; -import dev.skidfuscator.obfuscator.phantom.PhantomJarDownloader; -import dev.skidfuscator.obfuscator.phantom.PhantomResolvingJarDumper; +import dev.skidfuscator.obfuscator.phantom.jphantom.PhantomJarDownloader; +import dev.skidfuscator.obfuscator.phantom.jphantom.PhantomResolvingJarDumper; import lombok.SneakyThrows; -import org.mapleir.app.service.ApplicationClassSource; import org.mapleir.app.service.ClassTree; import org.mapleir.asm.ClassNode; import org.mapleir.asm.MethodNode; import org.mapleir.deob.PassGroup; -import org.mapleir.deob.passes.rename.ClassRenamerPass; import org.objectweb.asm.ClassWriter; -import org.topdank.byteengineer.commons.asm.DefaultASMFactory; +import org.topdank.byteengineer.commons.data.JarClassData; import org.topdank.byteengineer.commons.data.JarInfo; -import org.topdank.byteio.in.AbstractJarDownloader; import org.topdank.byteio.in.MultiJarDownloader; import org.topdank.byteio.in.SingleJarDownloader; +import org.topdank.byteio.in.SingleJmodDownloader; import java.io.*; import java.util.jar.JarEntry; @@ -30,61 +28,25 @@ public class MapleJarUtil { public static void dumpJar(Skidfuscator skidfuscator, PassGroup masterGroup, String outputFile) throws IOException { (new PhantomResolvingJarDumper(skidfuscator, skidfuscator.getJarDownloader().getJarContents(), skidfuscator.getClassSource()) { - @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("/", "."); - 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); - } - public int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOException { - JarEntry entry = new JarEntry(cn.getName() + ".class"); - out.putNextEntry(entry); + @Override + public int dumpClass(JarOutputStream out, JarClassData classData) throws IOException { + ClassNode cn = classData.getClassNode(); + JarEntry entry = new JarEntry(classData.getName()); - if (skidfuscator.getExemptAnalysis().isExempt(cn)) { - out.write( - skidfuscator + if (cn.isAnnoyingVersion()) { + final JarClassData resource = skidfuscator .getJarDownloader() .getJarContents() - .getClassData() - .namedMap().get(cn.getName() + ".class") - .getData() - ); + .getClassContents() + .namedMap() + .get(classData.getName()); + + if (resource == null) { + throw new IllegalStateException("Failed to find class source for " + cn.getName()); + } + out.putNextEntry(entry); + out.write(resource.getData()); return 1; } @@ -92,11 +54,12 @@ public int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOEx for (MethodNode m : cn.getMethods()) { if (m.node.instructions.size() > 10000) { - System.out.println("large method: " + m + " @" + m.node.instructions.size()); + System.out.println("\rlarge method: " + m + " @" + m.node.instructions.size() + "\n"); } } try { + out.putNextEntry(entry); try { ClassWriter writer = this.buildClassWriter(tree, ClassWriter.COMPUTE_FRAMES); cn.node.accept(writer); @@ -106,10 +69,10 @@ public int dumpClass(JarOutputStream out, String name, ClassNode cn) throws IOEx cn.node.accept(writer); out.write(writer.toByteArray()); var8.printStackTrace(); - System.err.println("Failed to write " + cn.getName() + "! Writing with COMPUTE_MAXS, which may cause runtime abnormalities"); + System.err.println("\rFailed to write " + cn.getName() + "! Writing with COMPUTE_MAXS, which may cause runtime abnormalities\n"); } } catch (Exception var9) { - System.err.println("Failed to write " + cn.getName() + "! Skipping class..."); + System.err.println("\rFailed to write " + cn.getName() + "! Skipping class...\n"); var9.printStackTrace(); } @@ -140,6 +103,14 @@ public static SingleJarDownloader importJar(File file) { return dl; } + @SneakyThrows + public static SingleJmodDownloader importJmod(File file) { + SingleJmodDownloader dl = new SingleJmodDownloader<>(new JarInfo(file)); + dl.download(); + + return dl; + } + @SneakyThrows public static PhantomJarDownloader importPhantomJar(File file, Skidfuscator skidfuscator) { PhantomJarDownloader dl = new PhantomJarDownloader<>( diff --git a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/ProgressUtil.java b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/ProgressUtil.java index 8c22994c..0e6db044 100644 --- a/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/ProgressUtil.java +++ b/obfuscator/src/main/java/dev/skidfuscator/obfuscator/util/ProgressUtil.java @@ -11,7 +11,7 @@ @UtilityClass public class ProgressUtil { public ProgressBar progress(final int count) { - return new ProgressBar("", + return new ProgressBar("Executing...", count, 1000, System.err, diff --git a/obfuscator/src/test/java/dev/skidfuscator/test/SampleJarTest.java b/obfuscator/src/test/java/dev/skidfuscator/test/SampleJarTest.java index ce62ab2d..6f5bdc03 100644 --- a/obfuscator/src/test/java/dev/skidfuscator/test/SampleJarTest.java +++ b/obfuscator/src/test/java/dev/skidfuscator/test/SampleJarTest.java @@ -2,9 +2,29 @@ import dev.skidfuscator.obfuscator.Skidfuscator; import dev.skidfuscator.obfuscator.SkidfuscatorSession; +import dev.xdark.ssvm.VirtualMachine; +import dev.xdark.ssvm.api.VMInterface; +import dev.xdark.ssvm.asm.Modifier; +import dev.xdark.ssvm.execution.VMException; +import dev.xdark.ssvm.fs.FileDescriptorManager; +import dev.xdark.ssvm.fs.HostFileDescriptorManager; +import dev.xdark.ssvm.jit.JitClass; +import dev.xdark.ssvm.jit.JitCompiler; +import dev.xdark.ssvm.jit.JitInstaller; +import dev.xdark.ssvm.mirror.InstanceJavaClass; +import dev.xdark.ssvm.mirror.JavaMethod; +import dev.xdark.ssvm.symbol.VMSymbols; +import dev.xdark.ssvm.util.VMHelper; +import dev.xdark.ssvm.value.InstanceValue; +import dev.xdark.ssvm.value.ObjectValue; +import dev.xdark.ssvm.value.Value; import org.junit.Test; +import org.objectweb.asm.MethodTooLargeException; +import org.objectweb.asm.tree.MethodNode; import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; /** * @author Ghast @@ -28,7 +48,93 @@ public void test2() throws Exception { final Skidfuscator skidfuscator = new Skidfuscator(session); skidfuscator.run(); - //Bootstrapper.main(new String[]{"C:\\Users\\sanja\\Documents\\GitHub\\SkidfuscatorV2\\dev.skidfuscator.obfuscator\\src\\test\\resources\\test.jar"}); + + // TODO: Fix SSVM + if (true) + return; + + VirtualMachine vm = new VirtualMachine(); + VMHelper helper = vm.getHelper(); + try { + VMInterface vmi = vm.getInterface(); + // Enable JIT, if needed + JitClassLoader definer = new JitClassLoader(); + vmi.registerMethodEnter( + ctx -> { + JavaMethod jm = ctx.getMethod(); + int count = jm.getInvocationCount(); + if (count == 256 && !Modifier.isCompiledMethod(jm.getAccess())) { + if (JitCompiler.isCompilable(jm)) { + try { + JitClass jit = JitCompiler.compile(jm, 3); + JitInstaller.install(jm, definer, jit); + } catch (MethodTooLargeException ex) { + MethodNode node = jm.getNode(); + node.access |= Modifier.ACC_JIT; + } catch (Throwable ex) { + throw new IllegalStateException("Could not install JIT class for " + jm, ex); + } + } + } + }); + // Bootstrap VM + vm.bootstrap(); + VMSymbols symbols = vm.getSymbols(); + + // Add jar to system class loader + Value cl = helper + .invokeStatic( + symbols.java_lang_ClassLoader(), + "getSystemClassLoader", + "()Ljava/lang/ClassLoader;", + new Value[0], + new Value[0]) + .getResult(); + assert cl instanceof ObjectValue : "ClassLoader must be ObjectValue"; + addURL(vm, cl, output.getPath()); + + // Invoke main, setup hooks to do stuff, etc + InstanceJavaClass klass = (InstanceJavaClass) helper.findClass((ObjectValue) cl, "dev.sim0n.evaluator.Main", true); + JavaMethod method = klass.getStaticMethod("main", "([Ljava/lang/String;)V"); + + helper.invokeStatic( + klass, method, new Value[0], new Value[] {helper.emptyArray(symbols.java_lang_String())}); + } catch (VMException ex) { + helper.invokeVirtual("printStackTrace", "()V", new Value[0], new Value[] {ex.getOop()}); + throw ex; + } + } + + private static final class JitClassLoader extends ClassLoader + implements JitInstaller.ClassDefiner { + + @Override + public Class define(JitClass jitClass) { + byte[] code = jitClass.getCode(); + return defineClass(jitClass.getClassName().replace('/', '.'), code, 0, code.length); + } + } + + private static void addURL(VirtualMachine vm, Value loader, String path) { + // ((URLClassLoader)loader).addURL(new File(path).toURI().toURL()); + VMHelper helper = vm.getHelper(); + InstanceJavaClass fileClass = (InstanceJavaClass) vm.findBootstrapClass("java/io/File", true); + InstanceValue file = vm.getMemoryManager().newInstance(fileClass); + helper.invokeExact( + fileClass, + "", + "(Ljava/lang/String;)V", + new Value[0], + new Value[] {file, helper.newUtf8(path)}); + Value uri = + helper + .invokeVirtual("toURI", "()Ljava/net/URI;", new Value[0], new Value[] {file}) + .getResult(); + Value url = + helper + .invokeVirtual("toURL", "()Ljava/net/URL;", new Value[0], new Value[] {uri}) + .getResult(); + helper.invokeVirtual("addURL", "(Ljava/net/URL;)V", new Value[0], new Value[] {loader, url}); } } diff --git a/obfuscator/src/test/resources/test-out.jar b/obfuscator/src/test/resources/test-out.jar index e7517662..41765c17 100644 Binary files a/obfuscator/src/test/resources/test-out.jar and b/obfuscator/src/test/resources/test-out.jar differ diff --git a/obfuscator/src/test/resources/test.jar b/obfuscator/src/test/resources/test.jar index 2bcdfc3f..97d08a8c 100644 Binary files a/obfuscator/src/test/resources/test.jar and b/obfuscator/src/test/resources/test.jar differ