diff --git a/SSVM b/SSVM new file mode 160000 index 0000000..0d4fc54 --- /dev/null +++ b/SSVM @@ -0,0 +1 @@ +Subproject commit 0d4fc5417a8a21b58dcd29849dac013aa00b4817 diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java index 206aa7b..739f9bc 100644 --- a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java @@ -9,6 +9,7 @@ import dev.skidfuscator.config.DefaultSkidConfig; import dev.skidfuscator.obfuscator.creator.SkidApplicationClassSource; import dev.skidfuscator.obfuscator.creator.SkidCache; +import dev.skidfuscator.obfuscator.creator.SkidFlowGraphDumper; import dev.skidfuscator.obfuscator.dependency.CommonDependency; import dev.skidfuscator.obfuscator.dependency.DependencyDownloader; import dev.skidfuscator.obfuscator.directory.SkiddedDirectory; @@ -682,7 +683,7 @@ protected List _loadTransformer() { public List getTransformers() { final List transformers = new ArrayList<>(); - if (true) { + if (!SkidFlowGraphDumper.TEST_COMPUTE) { if (tsConfig.hasPath("stringEncryption.type")) { switch (tsConfig.getEnum(StringEncryptionType.class, "stringEncryption.type")) { case STANDARD: transformers.add(new StringTransformerV2(this)); break; diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphDumper.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphDumper.java index bc9ee82..e79a2ff 100644 --- a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphDumper.java +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/creator/SkidFlowGraphDumper.java @@ -11,6 +11,7 @@ import dev.skidfuscator.obfuscator.skidasm.stmt.SkidBogusStmt; import dev.skidfuscator.obfuscator.util.TypeUtil; import dev.skidfuscator.obfuscator.util.misc.Parameter; +import dev.skidfuscator.obfuscator.verifier.Verifier; import org.mapleir.asm.ClassNode; import org.mapleir.flowgraph.ExceptionRange; import org.mapleir.flowgraph.edges.*; @@ -265,18 +266,24 @@ public void dump() { } maxLocal = Math.max(maxLocal, frameLocal.length); + maxStack = Math.max(maxStack, stackLength); lastFrame = frameLocal; lastStack = stack; - } for (Stmt stmt : b) { + if (stmt instanceof FrameStmt && TEST_COMPUTE) + continue; + stmt.toCode(m.node, this); } last = b; } + + m.node.visitMaxs(32, 32); + terminalLabel = new LabelNode(); m.node.visitLabel(terminalLabel.getLabel()); @@ -290,7 +297,7 @@ public void dump() { m.node.visitEnd(); - //Verifier.verify(m.node); + //Verifier.verify(m); } private Object _getFrameType(final Type type) { diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/verifier/CheckFrameAnalyzer.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/verifier/CheckFrameAnalyzer.java new file mode 100644 index 0000000..2b3b019 --- /dev/null +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/verifier/CheckFrameAnalyzer.java @@ -0,0 +1,483 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package dev.skidfuscator.obfuscator.verifier; + +import java.util.Collections; +import java.util.List; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FrameNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TableSwitchInsnNode; +import org.objectweb.asm.tree.TryCatchBlockNode; +import org.objectweb.asm.tree.TypeInsnNode; +import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.tree.analysis.AnalyzerException; +import org.objectweb.asm.tree.analysis.Frame; +import org.objectweb.asm.tree.analysis.Interpreter; +import org.objectweb.asm.tree.analysis.Value; + +/** + * An {@link Analyzer} subclass which checks that methods provide stack map frames where expected + * (i.e. at jump target and after instructions without immediate successor), and that these stack + * map frames are valid (for the provided interpreter; they may still be invalid for the JVM, if the + * {@link Interpreter} uses a simplified type system compared to the JVM verifier). This is done in + * two steps: + * + *
    + *
  • First, the stack map frames in {@link FrameNode}s are expanded, and stored at their + * respective instruction offsets. The expansion process uncompresses the APPEND, CHOP and + * SAME frames to FULL frames. It also converts the stack map frame verification types to + * {@link Value}s, via the provided {@link Interpreter}. The expansion is done in {@link + * #expandFrames}, by looking at each {@link FrameNode} in sequence (compressed frames are + * defined relatively to the previous {@link FrameNode}, or the implicit first frame). The + * actual decompression is done in {@link #expandFrame}, and the type conversion in {@link + * #newFrameValue}. + *
  • Next, the method instructions are checked in sequence. Starting from the implicit initial + * frame, the execution of each instruction i is simulated on the current stack map + * frame, with the {@link Frame#execute} method. This gives a new stack map frame f, + * representing the stack map frame state after the execution of i. Then: + *
      + *
    • If there is a next instruction and if the control flow cannot continue to it (e.g. if + * i is a RETURN or an ATHROW, for instance): an existing stack map frame + * f0 (coming from the first step) is expected after i. + *
    • If there is a next instruction and if the control flow can continue to it (e.g. if + * i is a ALOAD, for instance): either there an existing stack map frame + * f0 (coming from the first step) after i, or there is none. In the + * first case f and f0 must be compatible: the types in + * f must be sub types of the corresponding types in the existing frame + * f0 (otherwise an exception is thrown). In the second case, f0 is + * simply set to the value of f. + *
    • If the control flow can continue to some instruction j (e.g. if i + * is an IF_EQ, for instance): an existing stack map frame f0 (coming from the + * first step) is expected at j, which must be compatible with f (as + * defined previously). + *
    + * The sequential loop over the instructions is done in {@link #init}, which is called from + * the {@link Analyzer#analyze} method. Cases where the control flow cannot continue to the + * next instruction are handled in {@link #endControlFlow}. Cases where the control flow can + * continue to the next instruction, or jump to another instruction, are handled in {@link + * #checkFrame}. This method checks that an existing stack map frame is present when required, + * and checks the stack map frames compatibility with {@link #checkMerge}. + *
+ * + * @author Eric Bruneton + * @param type of the {@link Value} used for the analysis. + */ +public class CheckFrameAnalyzer extends Analyzer { + + /** The interpreter to use to symbolically interpret the bytecode instructions. */ + private final Interpreter interpreter; + + /** The instructions of the currently analyzed method. */ + private InsnList insnList; + + /** + * The number of locals in the last stack map frame processed by {@link expandFrame}. Long and + * double values are represented with two elements. + */ + private int currentLocals; + + CheckFrameAnalyzer(final Interpreter interpreter) { + super(interpreter); + this.interpreter = interpreter; + } + + @Override + protected void init(final String owner, final MethodNode method) throws AnalyzerException { + insnList = method.instructions; + currentLocals = Type.getArgumentsAndReturnSizes(method.desc) >> 2; + if ((method.access & Opcodes.ACC_STATIC) != 0) { + currentLocals -= 1; + } + + Frame[] frames = getFrames(); + Frame currentFrame = newFrame(frames[0]); + expandFrames(owner, method, currentFrame); + for (int insnIndex = 0; insnIndex < insnList.size(); ++insnIndex) { + Frame oldFrame = frames[insnIndex]; + + // Simulate the execution of this instruction. + AbstractInsnNode insnNode = null; + try { + insnNode = method.instructions.get(insnIndex); + int insnOpcode = insnNode.getOpcode(); + int insnType = insnNode.getType(); + + if (insnType == AbstractInsnNode.LABEL + || insnType == AbstractInsnNode.LINE + || insnType == AbstractInsnNode.FRAME) { + checkFrame(insnIndex + 1, oldFrame, /* requireFrame= */ false); + } else { + currentFrame.init(oldFrame).execute(insnNode, interpreter); + + if (insnNode instanceof JumpInsnNode) { + if (insnOpcode == JSR) { + throw new AnalyzerException(insnNode, "JSR instructions are unsupported"); + } + JumpInsnNode jumpInsn = (JumpInsnNode) insnNode; + int targetInsnIndex = insnList.indexOf(jumpInsn.label); + checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true); + if (insnOpcode == GOTO) { + endControlFlow(insnIndex); + } else { + checkFrame(insnIndex + 1, currentFrame, /* requireFrame= */ false); + } + } else if (insnNode instanceof LookupSwitchInsnNode) { + LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode; + int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt); + checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true); + for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) { + LabelNode label = lookupSwitchInsn.labels.get(i); + targetInsnIndex = insnList.indexOf(label); + currentFrame.initJumpTarget(insnOpcode, label); + checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true); + } + endControlFlow(insnIndex); + } else if (insnNode instanceof TableSwitchInsnNode) { + TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode; + int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt); + currentFrame.initJumpTarget(insnOpcode, tableSwitchInsn.dflt); + checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true); + newControlFlowEdge(insnIndex, targetInsnIndex); + for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) { + LabelNode label = tableSwitchInsn.labels.get(i); + currentFrame.initJumpTarget(insnOpcode, label); + targetInsnIndex = insnList.indexOf(label); + checkFrame(targetInsnIndex, currentFrame, /* requireFrame= */ true); + } + endControlFlow(insnIndex); + } else if (insnOpcode == RET) { + throw new AnalyzerException(insnNode, "RET instructions are unsupported"); + } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) { + checkFrame(insnIndex + 1, currentFrame, /* requireFrame= */ false); + } else { + endControlFlow(insnIndex); + } + } + + List insnHandlers = getHandlers(insnIndex); + if (insnHandlers != null) { + for (TryCatchBlockNode tryCatchBlock : insnHandlers) { + Type catchType; + if (tryCatchBlock.type == null) { + catchType = Type.getObjectType("java/lang/Throwable"); + } else { + catchType = Type.getObjectType(tryCatchBlock.type); + } + Frame handler = newFrame(oldFrame); + handler.clearStack(); + handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType)); + checkFrame(insnList.indexOf(tryCatchBlock.handler), handler, /* requireFrame= */ true); + } + } + + if (!hasNextJvmInsnOrFrame(insnIndex)) { + break; + } + } catch (AnalyzerException e) { + throw new AnalyzerException( + e.node, "Error at instruction " + insnIndex + ": " + e.getMessage() + "\n" + + "instruction: " + insnNode + "\n" + + "frame: " + currentFrame.toString() + , e); + } catch (RuntimeException e) { + // DontCheck(IllegalCatch): can't be fixed, for backward compatibility. + throw new AnalyzerException( + insnNode, "Error at instruction " + insnIndex + ": " + e.getMessage(), e); + } + } + } + + /** + * Expands the {@link FrameNode} "instructions" of the given method into {@link Frame} objects and + * stores them at the corresponding indices of the {@link #frames} array. The expanded frames are + * also associated with the label and line number nodes immediately preceding each frame node. + * + * @param owner the internal name of the class to which 'method' belongs. + * @param method the method whose frames must be expanded. + * @param initialFrame the implicit initial frame of 'method'. + * @throws AnalyzerException if the stack map frames of 'method', i.e. its FrameNode + * "instructions", are invalid. + */ + private void expandFrames( + final String owner, final MethodNode method, final Frame initialFrame) + throws AnalyzerException { + int lastJvmOrFrameInsnIndex = -1; + Frame currentFrame = initialFrame; + int currentInsnIndex = 0; + for (AbstractInsnNode insnNode : method.instructions) { + if (insnNode instanceof FrameNode) { + try { + currentFrame = expandFrame(owner, currentFrame, (FrameNode) insnNode); + } catch (AnalyzerException e) { + throw new AnalyzerException( + e.node, "Error at instruction " + currentInsnIndex + ": " + e.getMessage(), e); + } + for (int index = lastJvmOrFrameInsnIndex + 1; index <= currentInsnIndex; ++index) { + getFrames()[index] = currentFrame; + } + } + if (isJvmInsnNode(insnNode) || insnNode instanceof FrameNode) { + lastJvmOrFrameInsnIndex = currentInsnIndex; + } + currentInsnIndex += 1; + } + } + + /** + * Returns the expanded representation of the given {@link FrameNode}. + * + * @param owner the internal name of the class to which 'frameNode' belongs. + * @param previousFrame the frame before 'frameNode', in expanded form. + * @param frameNode a possibly compressed stack map frame. + * @return the expanded version of 'frameNode'. + * @throws AnalyzerException if 'frameNode' is invalid. + */ + private Frame expandFrame( + final String owner, final Frame previousFrame, final FrameNode frameNode) + throws AnalyzerException { + Frame frame = newFrame(previousFrame); + List locals = frameNode.local == null ? Collections.emptyList() : frameNode.local; + int currentLocal = currentLocals; + switch (frameNode.type) { + case Opcodes.F_NEW: + case Opcodes.F_FULL: + currentLocal = 0; + // fall through + case Opcodes.F_APPEND: + for (Object type : locals) { + V value = newFrameValue(owner, frameNode, type); + if (currentLocal + value.getSize() > frame.getLocals()) { + throw new AnalyzerException(frameNode, "Cannot append more locals than maxLocals"); + } + frame.setLocal(currentLocal++, value); + if (value.getSize() == 2) { + frame.setLocal(currentLocal++, interpreter.newValue(null)); + } + } + break; + case Opcodes.F_CHOP: + for (Object unusedType : locals) { + if (currentLocal <= 0) { + throw new AnalyzerException(frameNode, "Cannot chop more locals than defined"); + } + if (currentLocal > 1 && frame.getLocal(currentLocal - 2).getSize() == 2) { + currentLocal -= 2; + } else { + currentLocal -= 1; + } + } + break; + case Opcodes.F_SAME: + case Opcodes.F_SAME1: + break; + default: + throw new AnalyzerException(frameNode, "Illegal frame type " + frameNode.type); + } + currentLocals = currentLocal; + while (currentLocal < frame.getLocals()) { + frame.setLocal(currentLocal++, interpreter.newValue(null)); + } + + List stack = frameNode.stack == null ? Collections.emptyList() : frameNode.stack; + frame.clearStack(); + for (Object type : stack) { + frame.push(newFrameValue(owner, frameNode, type)); + } + return frame; + } + + /** + * Creates a new {@link Value} that represents the given stack map frame type. + * + * @param owner the internal name of the class to which 'frameNode' belongs. + * @param frameNode the stack map frame to which 'type' belongs. + * @param type an Integer, String or LabelNode object representing a primitive, reference or + * uninitialized a stack map frame type, respectively. See {@link FrameNode}. + * @return a value that represents the given type. + * @throws AnalyzerException if 'type' is an invalid stack map frame type. + */ + private V newFrameValue(final String owner, final FrameNode frameNode, final Object type) + throws AnalyzerException { + if (type == Opcodes.TOP) { + return interpreter.newValue(null); + } else if (type == Opcodes.INTEGER) { + return interpreter.newValue(Type.INT_TYPE); + } else if (type == Opcodes.FLOAT) { + return interpreter.newValue(Type.FLOAT_TYPE); + } else if (type == Opcodes.LONG) { + return interpreter.newValue(Type.LONG_TYPE); + } else if (type == Opcodes.DOUBLE) { + return interpreter.newValue(Type.DOUBLE_TYPE); + } else if (type == Opcodes.NULL) { + return interpreter.newOperation(new InsnNode(Opcodes.ACONST_NULL)); + } else if (type == Opcodes.UNINITIALIZED_THIS) { + return interpreter.newValue(Type.getObjectType(owner)); + } else if (type instanceof String) { + return interpreter.newValue(Type.getObjectType((String) type)); + } else if (type instanceof LabelNode) { + AbstractInsnNode referencedNode = (LabelNode) type; + while (referencedNode != null && !isJvmInsnNode(referencedNode)) { + referencedNode = referencedNode.getNext(); + } + if (referencedNode == null || referencedNode.getOpcode() != Opcodes.NEW) { + throw new AnalyzerException(frameNode, "LabelNode does not designate a NEW instruction"); + } + return interpreter.newValue(Type.getObjectType(((TypeInsnNode) referencedNode).desc)); + } + throw new AnalyzerException(frameNode, "Illegal stack map frame value " + type); + } + + /** + * Checks that the given frame is compatible with the frame at the given instruction index, if + * any. If there is no frame at this instruction index and none is required, the frame at + * 'insnIndex' is set to the given frame. Otherwise, if the merge of the two frames is not equal + * to the current frame at 'insnIndex', an exception is thrown. + * + * @param insnIndex an instruction index. + * @param frame a frame. This frame is left unchanged by this method. + * @param requireFrame whether a frame must already exist or not in {@link #frames} at + * 'insnIndex'. + * @throws AnalyzerException if the frames have incompatible sizes or if the frame at 'insnIndex' + * is missing (if required) or not compatible with 'frame'. + */ + private void checkFrame(final int insnIndex, final Frame frame, final boolean requireFrame) + throws AnalyzerException { + Frame oldFrame = getFrames()[insnIndex]; + if (oldFrame == null) { + if (requireFrame) { + throw new AnalyzerException(null, "Expected stack map frame at instruction " + insnIndex); + } + getFrames()[insnIndex] = newFrame(frame); + } else { + String error = checkMerge(frame, oldFrame); + if (error != null) { + throw new AnalyzerException( + null, + "Stack map frame incompatible with frame at instruction " + + insnIndex + + " (" + + error + + ")"); + } + } + } + + /** + * Checks that merging the two given frames would not produce any change, i.e. that the types in + * the source frame are sub types of the corresponding types in the destination frame. + * + * @param srcFrame a source frame. This frame is left unchanged by this method. + * @param dstFrame a destination frame. This frame is left unchanged by this method. + * @return an error message if the frames have incompatible sizes, or if a type in the source + * frame is not a sub type of the corresponding type in the destination frame. Returns + * {@literal null} otherwise. + */ + private String checkMerge(final Frame srcFrame, final Frame dstFrame) { + int numLocals = srcFrame.getLocals(); + if (numLocals != dstFrame.getLocals()) { + throw new AssertionError(); + } + for (int i = 0; i < numLocals; ++i) { + V v = interpreter.merge(srcFrame.getLocal(i), dstFrame.getLocal(i)); + if (!v.equals(dstFrame.getLocal(i))) { + return "incompatible types at local " + + i + + ": " + + srcFrame.getLocal(i) + + " and " + + dstFrame.getLocal(i); + } + } + int numStack = srcFrame.getStackSize(); + if (numStack != dstFrame.getStackSize()) { + return "incompatible stack heights"; + } + for (int i = 0; i < numStack; ++i) { + V v = interpreter.merge(srcFrame.getStack(i), dstFrame.getStack(i)); + if (!v.equals(dstFrame.getStack(i))) { + return "incompatible types at stack item " + + i + + ": " + + srcFrame.getStack(i) + + " and " + + dstFrame.getStack(i); + } + } + return null; + } + + /** + * Ends the control flow graph at the given instruction. This method checks that there is an + * existing frame for the next instruction, if any. + * + * @param insnIndex an instruction index. + * @throws AnalyzerException if 'insnIndex' is not the last instruction and there is no frame at + * 'insnIndex' + 1 in {@link #getFrames}. + */ + private void endControlFlow(final int insnIndex) throws AnalyzerException { + if (hasNextJvmInsnOrFrame(insnIndex) && getFrames()[insnIndex + 1] == null) { + throw new AnalyzerException( + null, "Expected stack map frame at instruction " + (insnIndex + 1)); + } + } + + /** + * Returns true if the given instruction is followed by a JVM instruction or a by stack map frame. + * + * @param insnIndex an instruction index. + * @return true if 'insnIndex' is followed by a JVM instruction or a by stack map frame. + */ + private boolean hasNextJvmInsnOrFrame(final int insnIndex) { + AbstractInsnNode insn = insnList.get(insnIndex).getNext(); + while (insn != null) { + if (isJvmInsnNode(insn) || insn instanceof FrameNode) { + return true; + } + insn = insn.getNext(); + } + return false; + } + + /** + * Returns true if the given instruction node corresponds to a real JVM instruction. + * + * @param insnNode an instruction node. + * @return true except for label, line number and stack map frame nodes. + */ + private static boolean isJvmInsnNode(final AbstractInsnNode insnNode) { + return insnNode.getOpcode() >= 0; + } +} diff --git a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/verifier/Verifier.java b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/verifier/Verifier.java index 07fb76c..afdf793 100644 --- a/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/verifier/Verifier.java +++ b/dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/verifier/Verifier.java @@ -1,13 +1,20 @@ package dev.skidfuscator.obfuscator.verifier; +import org.mapleir.asm.MethodNode; +import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.tree.analysis.BasicValue; import org.objectweb.asm.tree.analysis.BasicVerifier; +import org.objectweb.asm.util.CheckMethodAdapter; +import org.objectweb.asm.util.Printer; +import org.objectweb.asm.util.Textifier; +import org.objectweb.asm.util.TraceMethodVisitor; import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; public class Verifier { public static void verify(final ClassNode classNode) { @@ -23,8 +30,18 @@ public static void verify(final ClassNode classNode) { } public static void verify(final MethodNode methodNode) { try { - Analyzer analyzer = new Analyzer<>(new BasicVerifier()); - analyzer.analyzeAndComputeMaxs(methodNode.name, methodNode); + Textifier textifier = new Textifier(); + TraceMethodVisitor textifierVisitor = new TraceMethodVisitor(textifier); + methodNode.node.accept(textifierVisitor); + + System.out.println( + textifier.text.stream().map(Object::toString).collect(Collectors.joining("")) + ); + + Analyzer valueAnalyzer = new CheckFrameAnalyzer<>( + new BasicVerifier() + ); + valueAnalyzer.analyze(methodNode.getOwner(), methodNode.node); } catch (Exception e) { e.printStackTrace(); }